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

DataBinding fixing #1305

Closed

Conversation

BrianGardnerAtl
Copy link
Collaborator

@BrianGardnerAtl BrianGardnerAtl commented Feb 25, 2024

Related to #1016

  • Fix resolution of generated BR class when using the <data> tag
  • Refactor to generate the DataBindingMapperImpl instead of requiring consumers to add one themselves

Two issues I ran into trying to fix this. The first is that attempting to access any data binding from the test results in a class not found for DataBindingMapperImpl. One does get generated but it is not accessible. I found a similar issue related to Roboectric where one of the posts mentioned creating a fake version of the class and adding the actual version via reflection which worked.
Here is the example version.
I tweaked the implementation since we are unable to query the application package name from the unit test variant.

The second issue is that paparazzi is not loading the processed layout files which is why it's erroring when parsing the layout tag. I found an additional resource directory under build/intermediates/packaged_res/debug that has the processed layouts. Adding this to the projectResourceDirs in the paparazzi plugin allows the correct layout to load.
important this has to be the last resource directory. I tried having it first but it was defaulting to the original, unprocessed layout which fails on the layout tag.

* Must use kapt to access data binding classes in test
* Have to access another intermediate resource folder to get processed
  data binding layouts
* New resource folder must be last to have priority
import android.annotation.SuppressLint

/**
* Running instrumented tests with Robolectric on Data Binding enabled library modules throws [NoClassDefFoundError] for
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Snagged this from an associated thread about Robolectric having issues finding the same DataBinderMapperImpl. I can redo the comment here and link to the original thread for more context

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unfortunately this means clients would need to add this shim to their project as well which doesn't feel nice. Could we potentially do something with ByteBuddy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah that's a big concern. ByteBuddy could work as long as we can access the module's package to find the generated class

Copy link
Collaborator

Choose a reason for hiding this comment

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

So this is interesting. Note that in #1016, the repro project is an Android application, not a library project. This is because DataBinding generates DataBinderMapperImpl in the app module.

Now we could support that, but coupled with my comment about <data>, to what cause?

I'm of the opinion that we should strive to support ViewBinding style inflations, viaFooBinding.inflate(..), but not the full suite of possibilities that DataBinding offers. Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nonetheless, if the feeling is a resounding "yes, we should support databinding", then I think the way to tackle this unpleasant dev experience would be to check for the Gradle option being set in the buildFeatures, pass that as a System Property ("paparazzi.androidx.databinding.enabled") and load this impl in Paparazzi.kt when that property is set.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think we still need the DataBinderMapperImpl to support ViewBinding so I can look into setting up a system property

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
Copy link
Collaborator

@gamepro65 gamepro65 Feb 26, 2024

Choose a reason for hiding this comment

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

Have you tried using the <data/> tag?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not yet but I can add a test for that next

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think let's not support <data/> tags; I feel like it's a Pandora's box and not worth the squeeze.

The original issue in #1016 doesn't use it, and Paparazzi takes a fairly opinionated stance about separating business logic from view. Instead, users can grab a handle to textview and set their logic-driven properties directly in test code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think that would be fine. Data binding is in maintenance mode (according to this issue about migrating it to ksp) and it seems like google is pushing folks towards Compose instead. With this in mind, just supporting the view binding may be good enough

assertThat(config.projectResourceDirs).containsExactly(
"src/main/res",
"src/debug/res",
"build/intermediates/packaged_res/debug"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

agreed. I noticed similar, but I think our current AaptParser logic might need some refactoring. I'm exploring that now.

Copy link
Collaborator

@jrodbx jrodbx Feb 27, 2024

Choose a reason for hiding this comment

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

following up on this: I debugged how LayoutLib renders fragment_dashboard.xml (i.e., the repro from #1016 (comment)) in the IDE and this layout:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.dashboard.DashboardFragment">

        <TextView
            android:id="@+id/text_dashboard"
            ... />
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

results in this TagSnapshot hierarchy:

TagSnapshot{
  androidx.constraintlayout.widget.ConstraintLayout, 
  attributes=[
    AttributeSnapshot{layout_width="match_parent"}, 
    AttributeSnapshot{layout_height="match_parent"}, 
    AttributeSnapshot{context=".ui.dashboard.DashboardFragment"}, 
    AttributeSnapshot{tag="layout/fragment_dashboard_0"}
  ], 
  children=[
    TagSnapshot{
      TextView, 
        attributes=[
          AttributeSnapshot{id="@+id/text_dashboard"}, 
          ...
	      ], 
	      children=[]
    }
  ]
}

In particular, note the AttributeSnapshot{tag="layout/fragment_dashboard_0"}, which reminds me of the work we did in #196 to support Cash Card gradients.

@jrodbx
Copy link
Collaborator

jrodbx commented Feb 27, 2024

The second issue is that paparazzi is not loading the processed layout files which is why it's erroring when parsing the layout tag. I found an additional resource directory under build/intermediates/packaged_res/debug that has the processed layouts. Adding this to the projectResourceDirs in the paparazzi plugin allows the correct layout to load.

Yea, this will be partly fixed in #1136

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.

None yet

3 participants