mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Implemented drawable parser
This commit is contained in:
Binary file not shown.
+1
-2
@@ -45,8 +45,7 @@ class BitmapDrawableParser protected constructor(parser: XmlPullParser, minDepth
|
||||
}
|
||||
val `val` = value(index)
|
||||
val dr = parseDrawable(context, `val`)
|
||||
?: throw InflateException("Cannot parse drawable for android:src = $`val`")
|
||||
val bitmap = BitmapDrawable(BaseApplication.getBaseInstance().resources, toBitmap(dr))
|
||||
val bitmap = BitmapDrawable(context.resources, toBitmap(dr))
|
||||
index = attrIndex("antialias")
|
||||
if (index != -1) {
|
||||
bitmap.setAntiAlias(parseBoolean(value(index)))
|
||||
|
||||
+5
-9
@@ -20,7 +20,6 @@ import android.content.Context
|
||||
import android.graphics.drawable.ClipDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.Gravity
|
||||
import com.itsaky.androidide.inflater.InflateException
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
|
||||
/**
|
||||
@@ -31,17 +30,14 @@ import org.xmlpull.v1.XmlPullParser
|
||||
open class ClipDrawableParser protected constructor(parser: XmlPullParser, minDepth: Int) :
|
||||
IDrawableParser(parser, minDepth) {
|
||||
@Throws(Exception::class)
|
||||
public override fun parseDrawable(context: Context): Drawable {
|
||||
public override fun parseDrawable(context: Context): Drawable? {
|
||||
var index = attrIndex("drawable")
|
||||
if (index == -1) {
|
||||
throw InflateException("<clip> drawable must specify android:drawable attribute")
|
||||
// TODO Parse inner drawables
|
||||
return ClipDrawable(null, Gravity.LEFT, ClipDrawable.HORIZONTAL)
|
||||
}
|
||||
val value =
|
||||
value(index)
|
||||
?: throw InflateException("Invalid value specified to android:drawable attribute")
|
||||
val drawable =
|
||||
parseDrawable(context, value)
|
||||
?: throw InflateException("Unable to parse drawable for value: $value")
|
||||
val value = value(index)
|
||||
val drawable = parseDrawable(context, value)
|
||||
var orientation = ClipDrawable.HORIZONTAL
|
||||
index = attrIndex("clipOrientation")
|
||||
if (index != -1) {
|
||||
|
||||
+33
-33
@@ -21,7 +21,6 @@ import android.graphics.drawable.Drawable
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import androidx.annotation.ChecksSdkIntAtLeast
|
||||
import com.itsaky.androidide.inflater.internal.utils.parseDimension
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
|
||||
/**
|
||||
@@ -29,7 +28,8 @@ import org.xmlpull.v1.XmlPullParser
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class IDrawableParser protected constructor(protected open val parser: XmlPullParser?, open var minDepth: Int) {
|
||||
abstract class IDrawableParser
|
||||
protected constructor(protected open val parser: XmlPullParser?, open var minDepth: Int) {
|
||||
/**
|
||||
* Parse the drawable using the already provided parser and data.
|
||||
*
|
||||
@@ -61,40 +61,41 @@ abstract class IDrawableParser protected constructor(protected open val parser:
|
||||
}
|
||||
return drawable
|
||||
}
|
||||
|
||||
protected fun parseInteger(value: String?, def: Int): Int {
|
||||
return parseInteger(value, def)
|
||||
|
||||
protected fun parseInteger(value: String, def: Int): Int {
|
||||
return com.itsaky.androidide.inflater.internal.utils.parseInteger(value, def)
|
||||
}
|
||||
|
||||
protected fun parseBoolean(value: String?): Boolean {
|
||||
return parseBoolean(value)
|
||||
|
||||
protected fun parseBoolean(value: String): Boolean {
|
||||
return com.itsaky.androidide.inflater.internal.utils.parseBoolean(value)
|
||||
}
|
||||
|
||||
protected fun parseDrawable(context: Context?, value: String?): Drawable {
|
||||
return parseDrawable(context, value)
|
||||
|
||||
protected fun parseDrawable(context: Context, value: String): Drawable {
|
||||
return com.itsaky.androidide.inflater.internal.utils.parseDrawable(context, value)
|
||||
}
|
||||
|
||||
protected fun parseGravity(value: String?): Int {
|
||||
return parseGravity(value)
|
||||
|
||||
protected fun parseGravity(value: String): Int {
|
||||
return com.itsaky.androidide.inflater.internal.utils.parseGravity(value)
|
||||
}
|
||||
|
||||
|
||||
protected fun parseDimension(context: Context?, value: String?): Int {
|
||||
return parseDimension(context!!, value, 0f).toInt()
|
||||
return com.itsaky.androidide.inflater.internal.utils
|
||||
.parseDimension(context!!, value, 0f)
|
||||
.toInt()
|
||||
}
|
||||
|
||||
protected fun parseColor(context: Context?, value: String?): Int {
|
||||
return parseColor(context, value)
|
||||
|
||||
protected fun parseColor(context: Context, value: String): Int {
|
||||
return com.itsaky.androidide.inflater.internal.utils.parseColor(context, value)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actual implementation of the parse logic.
|
||||
*
|
||||
* @return The parsed drawable. Maybe `null`.
|
||||
* @throws Exception If any fatal error occurs while parsing the drawable.
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
protected abstract fun parseDrawable(context: Context): Drawable?
|
||||
|
||||
@Throws(Exception::class) protected abstract fun parseDrawable(context: Context): Drawable?
|
||||
|
||||
/**
|
||||
* Find the index of the attribute with the given name.
|
||||
*
|
||||
@@ -109,7 +110,7 @@ abstract class IDrawableParser protected constructor(protected open val parser:
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of the attribute at the given index.
|
||||
*
|
||||
@@ -119,28 +120,27 @@ abstract class IDrawableParser protected constructor(protected open val parser:
|
||||
protected fun value(index: Int): String {
|
||||
return parser!!.getAttributeValue(index)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the drawable parser is allowed to parse at the current depth. This must be checked if
|
||||
* the drawable parser keeps looking for [XmlPullParser.START_TAG] or [ ][XmlPullParser.END_TAG].
|
||||
*
|
||||
*
|
||||
* The [LayerListParser] can contain tags which are expected to be parsed by other
|
||||
* parsers. So, if the nesting parser does not check if this method if `true`, it might
|
||||
* consume all the events and the [LayerListParser] will have invalid data to parse. In some
|
||||
* cases, the nested parser might consume all the events until [XmlPullParser.END_DOCUMENT].
|
||||
* The [LayerListParser] can contain tags which are expected to be parsed by other parsers. So, if
|
||||
* the nesting parser does not check if this method if `true`, it might consume all the events and
|
||||
* the [LayerListParser] will have invalid data to parse. In some cases, the nested parser might
|
||||
* consume all the events until [XmlPullParser.END_DOCUMENT].
|
||||
*
|
||||
* @return `true` if the parser can keep parsing, `false` otherwise.
|
||||
*/
|
||||
protected fun canParse(): Boolean {
|
||||
return parser!!.depth >= minDepth
|
||||
}
|
||||
|
||||
|
||||
@get:ChecksSdkIntAtLeast(api = VERSION_CODES.Q)
|
||||
protected val isApi29: Boolean
|
||||
get() = VERSION.SDK_INT >= 29
|
||||
|
||||
|
||||
companion object {
|
||||
const val ANY_DEPTH = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-14
@@ -25,12 +25,14 @@ import android.util.TypedValue.complexToDimension
|
||||
import android.view.Gravity
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.android.SdkConstants.EXT_XML
|
||||
import com.android.aaptcompiler.AaptResourceType
|
||||
import com.android.aaptcompiler.AaptResourceType.ARRAY
|
||||
import com.android.aaptcompiler.AaptResourceType.ATTR
|
||||
import com.android.aaptcompiler.AaptResourceType.BOOL
|
||||
import com.android.aaptcompiler.AaptResourceType.COLOR
|
||||
import com.android.aaptcompiler.AaptResourceType.DIMEN
|
||||
import com.android.aaptcompiler.AaptResourceType.DRAWABLE
|
||||
import com.android.aaptcompiler.AaptResourceType.INTEGER
|
||||
import com.android.aaptcompiler.AaptResourceType.STRING
|
||||
import com.android.aaptcompiler.ArrayResource
|
||||
@@ -38,6 +40,7 @@ import com.android.aaptcompiler.AttributeResource
|
||||
import com.android.aaptcompiler.BasicString
|
||||
import com.android.aaptcompiler.BinaryPrimitive
|
||||
import com.android.aaptcompiler.ConfigDescription
|
||||
import com.android.aaptcompiler.FileReference
|
||||
import com.android.aaptcompiler.RawString
|
||||
import com.android.aaptcompiler.Reference
|
||||
import com.android.aaptcompiler.ResourceEntry
|
||||
@@ -52,8 +55,10 @@ import com.android.aaptcompiler.tryParseBool
|
||||
import com.android.aaptcompiler.tryParseFlagSymbol
|
||||
import com.android.aaptcompiler.tryParseInt
|
||||
import com.android.aaptcompiler.tryParseReference
|
||||
import com.itsaky.androidide.inflater.drawable.DrawableParserFactory
|
||||
import com.itsaky.androidide.projects.api.AndroidModule
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
private var currentModule: AndroidModule? = null
|
||||
@@ -171,15 +176,27 @@ fun parseDrawable(context: Context, value: String, def: Drawable = unknownDrawab
|
||||
if (HEX_COLOR.matcher(value).matches()) {
|
||||
return parseColorDrawable(context, value)
|
||||
}
|
||||
|
||||
// if (value[0] == '@') {
|
||||
// val (pck, type, name) = parseResourceReference(value)
|
||||
// return if(pck == null) {
|
||||
// parseDrawableResRef(type, name, value)
|
||||
// } else {
|
||||
// parseQualifiedDrawableResRef()
|
||||
// }
|
||||
// }
|
||||
val drawableResolver: (Value?) -> Drawable? =
|
||||
fun(it): Drawable? {
|
||||
if (it is FileReference) {
|
||||
val file = File(it.path.value())
|
||||
if (!file.exists() || file.extension != EXT_XML) {
|
||||
return null
|
||||
}
|
||||
val parser = DrawableParserFactory.newParser(context, file) ?: return null
|
||||
return parser.parse(context)
|
||||
}
|
||||
// TODO(itsaky) : Drawable of any type other than a file?
|
||||
return null
|
||||
}
|
||||
if (value[0] == '@') {
|
||||
return parseReference(
|
||||
value = value,
|
||||
expectedType = DRAWABLE,
|
||||
def = def,
|
||||
resolver = drawableResolver
|
||||
)
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
@@ -278,7 +295,7 @@ fun parseFloat(value: String, defValue: Float): Float {
|
||||
@JvmOverloads
|
||||
fun parseGravity(value: String, def: Int = defaultGravity()): Int {
|
||||
val attr = findAttributeResource("android", ATTR, "gravity") ?: return defaultGravity()
|
||||
return parseFlag(attr = attr, value = value, def = defaultGravity())
|
||||
return parseFlag(attr = attr, value = value, def = def)
|
||||
}
|
||||
|
||||
fun parseFlag(attr: AttributeResource, value: String, def: Int = -1): Int {
|
||||
@@ -389,19 +406,19 @@ fun <T> resolveResourceReference(
|
||||
def: T,
|
||||
resolver: (Value?) -> T?
|
||||
): T {
|
||||
val dimenValue = entry.findValue(ConfigDescription())!!.value
|
||||
if (dimenValue is Reference) {
|
||||
val value = entry.findValue(ConfigDescription())!!.value
|
||||
if (value is Reference) {
|
||||
return resolveResourceReference(
|
||||
table = table,
|
||||
type = type,
|
||||
pck = pck.name,
|
||||
name = dimenValue.name.entry!!,
|
||||
name = value.name.entry!!,
|
||||
def = def,
|
||||
resolver = resolver
|
||||
)
|
||||
}
|
||||
|
||||
return resolver(dimenValue)
|
||||
return resolver(value)
|
||||
?: run {
|
||||
log.warn("Unable to resolve dimension reference '$name'")
|
||||
def
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
package com.itsaky.androidide.inflater
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.ClipDrawable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.itsaky.androidide.inflater.internal.utils.endParse
|
||||
import com.itsaky.androidide.inflater.internal.utils.parseBoolean
|
||||
@@ -30,10 +35,10 @@ import com.itsaky.androidide.inflater.internal.utils.parseIntegerArray
|
||||
import com.itsaky.androidide.inflater.internal.utils.parseString
|
||||
import com.itsaky.androidide.inflater.internal.utils.parseStringArray
|
||||
import com.itsaky.androidide.inflater.internal.utils.startParse
|
||||
import kotlin.math.roundToInt
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class ValueParsersTest {
|
||||
@@ -85,7 +90,7 @@ class ValueParsersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `simple color drawable parse test`() {
|
||||
fun `drawable parse test`() {
|
||||
inflaterTest { module ->
|
||||
requiresActivity { activity ->
|
||||
startParse(module)
|
||||
@@ -94,6 +99,14 @@ class ValueParsersTest {
|
||||
assertThat(this).isInstanceOf(ColorDrawable::class.java)
|
||||
assertThat((this as ColorDrawable).color).isEqualTo(Color.RED)
|
||||
}
|
||||
parseDrawable(activity, "@android:drawable/ab_share_pack_material").apply {
|
||||
assertThat(this).isNotNull()
|
||||
assertThat(this).isInstanceOf(BitmapDrawable::class.java)
|
||||
}
|
||||
parseDrawable(activity, "@android:drawable/action_bar_background").apply {
|
||||
assertThat(this).isNotNull()
|
||||
assertThat(this).isInstanceOf(GradientDrawable::class.java)
|
||||
}
|
||||
endParse()
|
||||
}
|
||||
}
|
||||
@@ -191,8 +204,9 @@ class ValueParsersTest {
|
||||
assertThat(parseColor(activity, "@android:color/white")).isEqualTo(Color.WHITE)
|
||||
assertThat(parseColor(activity, "@android:color/transparent")).isEqualTo(Color.TRANSPARENT)
|
||||
assertThat(parseColor(activity, "@color/test_color")).isEqualTo(Color.parseColor("#f44336"))
|
||||
assertThat(parseColor(activity, "@color/test_color_ref")).isEqualTo(Color.parseColor("#f44336"))
|
||||
|
||||
assertThat(parseColor(activity, "@color/test_color_ref"))
|
||||
.isEqualTo(Color.parseColor("#f44336"))
|
||||
|
||||
// TODO Implement color state list parser
|
||||
assertThat(parseColor(activity, "@color/test_selector")).isEqualTo(Color.TRANSPARENT)
|
||||
endParse()
|
||||
|
||||
Reference in New Issue
Block a user