All Downloads are FREE. Search and download functionalities are using the official Maven repository.

app.cash.paparazzi.gradle.instrumentation.ResourcesCompatVisitorFactory.kt Maven / Gradle / Ivy

The newest version!
package app.cash.paparazzi.gradle.instrumentation

import com.android.build.api.instrumentation.AsmClassVisitorFactory
import com.android.build.api.instrumentation.ClassContext
import com.android.build.api.instrumentation.ClassData
import com.android.build.api.instrumentation.InstrumentationParameters
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes

internal abstract class ResourcesCompatVisitorFactory : AsmClassVisitorFactory {
  override fun createClassVisitor(
    classContext: ClassContext,
    nextClassVisitor: ClassVisitor
  ): ClassVisitor {
    return ResourcesCompatTransform(nextClassVisitor)
  }

  override fun isInstrumentable(classData: ClassData): Boolean =
    classData.className == RESOURCES_COMPAT_CLASS_NAME

  /**
   * [ClassVisitor] that fixes a hardcoded path in ResourcesCompat.loadFont.
   * In this method, there is a check that font files have a path that starts with res/.
   * This is not the case in Studio. This replaces the check with one that verifies that the path contains res/.
   *
   * Inspired by ASM fix:
   * https://cs.android.com/android-studio/platform/tools/adt/idea/+/mirror-goog-studio-main:rendering/src/com/android/tools/rendering/classloading/ResourcesCompatTransform.kt;drc=5bb41b6d5e519c891a4cd6149234138faa28e1af
   */
  internal class ResourcesCompatTransform(delegate: ClassVisitor) : ClassVisitor(Opcodes.ASM9, delegate) {
    override fun visitMethod(
      access: Int,
      name: String,
      descriptor: String,
      signature: String?,
      exceptions: Array?
    ): MethodVisitor {
      val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)
      if (name == LOAD_FONT_METHOD_NAME && descriptor == LOAD_FONT_METHOD_DESCRIPTOR) {
        return LoadFontVisitor(api, methodVisitor)
      }
      return methodVisitor
    }

    private class LoadFontVisitor(api: Int, delegate: MethodVisitor) : MethodVisitor(api, delegate) {
      override fun visitMethodInsn(
        opcode: Int,
        owner: String,
        name: String,
        descriptor: String,
        isInterface: Boolean
      ) {
        if ("startsWith" == name) {
          super.visitMethodInsn(opcode, owner, "contains", "(Ljava/lang/CharSequence;)Z", isInterface)
        } else {
          super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
        }
      }
    }
  }

  internal companion object {
    const val RESOURCES_COMPAT_CLASS_NAME = "androidx.core.content.res.ResourcesCompat"
    const val LOAD_FONT_METHOD_NAME = "loadFont"
    const val LOAD_FONT_METHOD_DESCRIPTOR =
      "(Landroid/content/Context;Landroid/content/res/Resources;Landroid/util/TypedValue;IILandroidx/core/content/res/ResourcesCompat\$FontCallback;Landroid/os/Handler;ZZ)Landroid/graphics/Typeface;"
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy