material-components-android icon indicating copy to clipboard operation
material-components-android copied to clipboard

[TabLayout] Default tab text appearance is incorrectly setting attributes for text appearance instead of style

Open geoff-powell opened this issue 2 years ago • 3 comments
trafficstars

Description: When updating to 1.9.0 of material, we noticed an error when taking Paparazzi snapshot tests using a TabLayout. There was a change from 1.7.0 -> 1.9.0 where defaultTabTextAppearance was used.

Stacktrace

Jun 06, 2023 3:19:08 PM app.cash.paparazzi.internal.PaparazziLogger error
SEVERE: null: Style ResourceReference{namespace=apk/res-auto, type=attr, name=textAppearanceButton} is not of type STYLE (instead attr)
Jun 06, 2023 3:19:08 PM app.cash.paparazzi.internal.PaparazziLogger error
SEVERE: null: Style ResourceReference{namespace=apk/res-auto, type=attr, name=textAppearanceButton} is not of type STYLE (instead attr)
Jun 06, 2023 3:19:08 PM app.cash.paparazzi.internal.PaparazziLogger error
SEVERE: resources.resolve: Failed to find style with 2130969720

Cannot invoke "android.content.res.BridgeTypedArray.setTheme(android.content.res.Resources$Theme)" because "ta" is null
java.lang.NullPointerException: Cannot invoke "android.content.res.BridgeTypedArray.setTheme(android.content.res.Resources$Theme)" because "ta" is null
	at android.content.res.Resources_Theme_Delegate.obtainStyledAttributes(Resources_Theme_Delegate.java:74)
	at android.content.res.Resources$Theme.obtainStyledAttributes(Resources.java:1631)
	at android.content.Context.obtainStyledAttributes(Context.java:874)
	at android.widget.TextView.setTextAppearance(TextView.java:3965)
	at androidx.appcompat.widget.AppCompatTextView.setTextAppearance(AppCompatTextView.java:216)
	at android.widget.TextView.setTextAppearance(TextView.java:3954)
	at androidx.core.widget.TextViewCompat.setTextAppearance(TextViewCompat.java:289)
	...
	at com.google.testing.junit.testparameterinjector.PluggableTestRunner$ContextMethodRule$1.evaluate(PluggableTestRunner.java:429)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:108)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)

Expected behavior: Correctly load styles from attributes using something like this

val attrs = intArrayOf(defaultTabTextAppearance)       // The array of attributes we're interested in.
val ta = context.obtainStyledAttributes(attrs)        // Get the value referenced by the attributes in the array
val resId = ta.getResourceId(0, 0)                    // The first 0 is the index in the 'attrs' array.
ta.recycle()                                          // Don't forget that! You can also use TypedArray.use { } extensions from android KTX.
TextViewCompat.setTextAppearance(textView, resId)     // Utility method to set text appearance for all SDK versions

Source code: The code snippet which is causing this issue

class SampleTest {
  @get:Rule
  val paparazzi = Paparazzi(
    deviceConfig = DeviceConfig.PIXEL_2.copy(fontScale = accessibilityTextSize.scale),
  )

  private val context: Context by lazy { paparazzi.context.wrapWithTheme { design.provide(this) } }

  @Test
  fun default() {
    val view = TabLayout(context, null)
    println("textAppearanceButton ID: " + com.google.android.material.R.attr.textAppearanceButton)
    TextViewCompat.setTextAppearance(textView, com.google.android.material.R.attr.textAppearanceButton)

    paparazzi.snapshot(view)
  }
}

Minimal sample app repro: TBD

Android API version: 32 (via Paparazzi)

Material Library version: 1.9.0

Device: (Run via junit tests with Paparazzi)

geoff-powell avatar Jun 06 '23 19:06 geoff-powell

Same problem. Also happens in version 1.8.0.

Bunch of render problems with this simple XML code:

<com.google.android.material.tabs.TabLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tab 1" />

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tab 2" />

    <com.google.android.material.tabs.TabItem
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tab 3" />
</com.google.android.material.tabs.TabLayout>

Style ResourceReference{namespace=apk/res-auto, type=attr, name=textAppearanceTitleSmall} is not of type STYLE (instead attr)

java.lang.NullPointerException: Cannot invoke "android.content.res.BridgeTypedArray.setTheme(android.content.res.Resources$Theme)" because "ta" is null

Asuveroz avatar Jun 14 '23 08:06 Asuveroz

Yes the version problem ; try switch to implementation "com.google.android.material:material:1.7.0"

TomGarden avatar Jul 28 '23 11:07 TomGarden

This is still an issue with build 1.11.0

hamidreza-sani avatar Jan 04 '24 20:01 hamidreza-sani