Fixed dimension parser

This commit is contained in:
Akash Yadav
2022-11-05 16:22:35 +05:30
parent a1f4ded445
commit 1576fffe4a
5 changed files with 58 additions and 57 deletions
@@ -134,6 +134,10 @@ data class ResValue(val dataType: DataType, val data: Int, val size: Short = 0)
const val UNIT_SHIFT = 0
const val UNIT_MASK = 0xf
// ==================================
// The values here are same as specified in TypedValue
// ==================================
// TYPE_DIMENSION: Value is raw pixels.
const val UNIT_PX = 0
// TYPE_DIMENSION: Value is Device Independent Pixels.
Binary file not shown.
@@ -32,6 +32,7 @@ import com.itsaky.androidide.inflater.internal.utils.startParse
import com.itsaky.androidide.projects.ProjectManager
import com.itsaky.androidide.projects.api.AndroidModule
import java.io.File
import kotlin.math.roundToInt
/**
* Attribute adapter for [View].
@@ -55,7 +56,7 @@ open class ViewAttrAdapter : IAttributeAdapter() {
}
protected fun parseDimension(context: Context, value: String): Int {
return com.itsaky.androidide.inflater.internal.utils.parseDimension(context, value)
return com.itsaky.androidide.inflater.internal.utils.parseDimension(context, value).roundToInt()
}
protected fun parseFloat(value: String, def: Float = 1f): Float {
@@ -21,13 +21,14 @@ import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.util.TypedValue
import android.util.TypedValue.COMPLEX_UNIT_DIP
import android.util.TypedValue.COMPLEX_UNIT_IN
import android.util.TypedValue.COMPLEX_UNIT_MM
import android.util.TypedValue.COMPLEX_UNIT_PT
import android.util.TypedValue.COMPLEX_UNIT_PX
import android.util.TypedValue.COMPLEX_UNIT_SP
import android.util.TypedValue.applyDimension
import android.util.TypedValue.complexToDimension
import android.view.ViewGroup.LayoutParams
import androidx.core.text.isDigitsOnly
import com.android.aaptcompiler.AaptResourceType
@@ -42,6 +43,8 @@ import com.android.aaptcompiler.ResourceName
import com.android.aaptcompiler.ResourceTable
import com.android.aaptcompiler.ResourceTablePackage
import com.android.aaptcompiler.Value
import com.android.aaptcompiler.android.ResValue.DataType.DIMENSION
import com.android.aaptcompiler.android.stringToFloat
import com.itsaky.androidide.projects.api.AndroidModule
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.xml.utils.attrValue_qualifiedRef
@@ -185,25 +188,28 @@ fun newColorDrawable(color: Int): Drawable {
fun parseDimension(
context: Context,
value: String?,
def: Int = LayoutParams.WRAP_CONTENT,
): Int {
def: Float = LayoutParams.WRAP_CONTENT.toFloat(),
): Float {
if (value.isNullOrBlank()) {
return def
}
val displayMetrics = context.resources.displayMetrics
val c = value[0]
if (c.isDigit()) {
val i = value.length - 2
val dimension = parseFloat(value.substring(0, i), def.toFloat())
val unit = parseDimensionUnit(value.substring(i))
return applyDimension(unit, dimension, context.resources.displayMetrics).toInt()
val (dataType, data, _) = stringToFloat(value) ?: return def
if (dataType != DIMENSION) {
return def
}
return complexToDimension(data, displayMetrics)
} else if (c == '@') {
val (pck, type, name) = parseResourceReference(value)
if (type != "dimen") {
throwInvalidResType(DIMEN, value)
}
val resolver: (Value?) -> Int? = {
val resolver: (Value?) -> Float? = {
if (it is BinaryPrimitive) {
it.resValue.data
complexToDimension(it.resValue.data, displayMetrics)
// TODO handle other resource types
} else null
}
@@ -226,9 +232,9 @@ fun parseDimension(
}
} else {
return when (value) {
"wrap_content" -> LayoutParams.WRAP_CONTENT
"wrap_content" -> LayoutParams.WRAP_CONTENT.toFloat()
"fill_parent",
"match_parent", -> LayoutParams.MATCH_PARENT
"match_parent", -> LayoutParams.MATCH_PARENT.toFloat()
else -> {
log.warn("Cannot infer type of dimension resource: '$value'")
def
@@ -245,18 +251,6 @@ fun parseFloat(value: String, defValue: Float): Float {
}
}
fun parseDimensionUnit(unitStr: String): Int {
return when (unitStr) {
"dp" -> COMPLEX_UNIT_DIP
"sp" -> COMPLEX_UNIT_SP
"pt" -> COMPLEX_UNIT_PT
"px" -> COMPLEX_UNIT_PX
"in" -> COMPLEX_UNIT_IN
"mm" -> COMPLEX_UNIT_MM
else -> COMPLEX_UNIT_DIP
}
}
fun <T> resolveUnqualifiedResourceReference(
type: AaptResourceType,
name: String,
@@ -29,6 +29,7 @@ import com.itsaky.androidide.inflater.internal.utils.startParse
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import kotlin.math.roundToInt
@RunWith(RobolectricTestRunner::class)
class ValueParsersTest {
@@ -43,36 +44,36 @@ class ValueParsersTest {
val units = arrayOf("dp", "sp", "pt", "px", "in", "mm")
val results = intArrayOf(1, 1, 2, 1, 160, 6)
for (i in units.indices) {
assertThat(parseDimension(activity, "1" + units[i], -1)).isEqualTo(results[i])
assertThat(parseDimension(activity, "1" + units[i], -1f).roundToInt()).isEqualTo(results[i])
}
// Dimensions from platform resources
assertThat(parseDimension(activity, "@android:dimen/app_icon_size")).isEqualTo(12289)
assertThat(parseDimension(activity, "@android:dimen/app_icon_size")).isEqualTo(48)
assertThat(parseDimension(activity, "@android:dimen/status_bar_height_portrait"))
.isEqualTo(6145)
.isEqualTo(24)
val exists = try {
// The app module here targets android 31 and the dimension resource below was added in
// android 31. So, the resource should not be resolved and
// ViewGroup.LayoutParams.WRAP_CONTENT must be returned instead.
assertThat(parseDimension(activity, "@android:dimen/status_bar_height_default"))
.isEqualTo(-2)
true
} catch (err: IllegalArgumentException) {
false
}
val exists =
try {
// The app module here targets android 31 and the dimension resource below was added in
// android 31. So, this should throw an exception
assertThat(parseDimension(activity, "@android:dimen/status_bar_height_default"))
.isEqualTo(-2)
true
} catch (err: IllegalArgumentException) {
false
}
assertThat(exists).isFalse()
assertThat(parseDimension(activity, "@dimen/test_dimen_0dp", def = 1)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_1dp", def = 0)).isEqualTo(1)
assertThat(parseDimension(activity, "@dimen/test_dimen_1dp_ref", def = 0)).isEqualTo(1)
assertThat(parseDimension(activity, "@dimen/test_dimen_0dp_ref", def = 0)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_0dp", def = 1f)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_1dp", def = 0f)).isEqualTo(1)
assertThat(parseDimension(activity, "@dimen/test_dimen_1dp_ref", def = 0f)).isEqualTo(1)
assertThat(parseDimension(activity, "@dimen/test_dimen_0dp_ref", def = 0f)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_0pt", def = 1)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_1pt", def = 0)).isEqualTo(2)
assertThat(parseDimension(activity, "@dimen/test_dimen_1pt_ref", def = 0)).isEqualTo(2)
assertThat(parseDimension(activity, "@dimen/test_dimen_0pt_ref", def = 0)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_0pt", def = 1f)).isEqualTo(0)
assertThat(parseDimension(activity, "@dimen/test_dimen_1pt", def = 0f)).isEqualTo(1)
assertThat(parseDimension(activity, "@dimen/test_dimen_1pt_ref", def = 0f)).isEqualTo(1)
assertThat(parseDimension(activity, "@dimen/test_dimen_0pt_ref", def = 0f)).isEqualTo(0)
endParse()
}
}
@@ -96,7 +97,7 @@ class ValueParsersTest {
@Test
fun `boolean parser test`() {
inflaterTest { module ->
requiresActivity { activity ->
requiresActivity {
startParse(module)
assertThat(parseBoolean("true")).isTrue()
assertThat(parseBoolean("false", def = true)).isFalse()
@@ -114,12 +115,13 @@ class ValueParsersTest {
@Test
fun `integer parser test`() {
inflaterTest { module ->
requiresActivity { activity ->
requiresActivity {
startParse(module)
assertThat(parseInteger("0", def = 1)).isEqualTo(0)
assertThat(parseInteger("10")).isEqualTo(10)
assertThat(parseInteger("110")).isEqualTo(110)
assertThat(parseInteger("@android:integer/button_pressed_animation_duration")).isEqualTo(100)
assertThat(parseInteger("@android:integer/button_pressed_animation_duration"))
.isEqualTo(100)
assertThat(parseInteger("@android:integer/dock_enter_exit_duration")).isEqualTo(250)
assertThat(parseInteger("@android:integer/kg_carousel_angle")).isEqualTo(75)
assertThat(parseInteger("@android:integer/date_picker_mode")).isEqualTo(1)