diff --git a/build-tools/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt b/build-tools/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt index 47b45cd1e..519bfd359 100755 --- a/build-tools/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt +++ b/build-tools/aaptcompiler/src/main/java/com/android/aaptcompiler/android/ResourceTypes.kt @@ -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. @@ -678,4 +682,4 @@ fun stringToFloat(string: String): ResValue? { } return ResValue(ResValue.DataType.FLOAT, parsedValue.toRawBits()) -} +} \ No newline at end of file diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar index ffc72594a..4e933698f 100644 Binary files a/tests/test-home/.androidide/init/model.jar and b/tests/test-home/.androidide/init/model.jar differ diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt index 5b76af0f8..22daa4b67 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ViewAttrAdapter.kt @@ -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 { diff --git a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/parseUtils.kt b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/parseUtils.kt index dee711a6f..817553332 100644 --- a/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/parseUtils.kt +++ b/xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/utils/parseUtils.kt @@ -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 resolveUnqualifiedResourceReference( type: AaptResourceType, name: String, diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt index d521dab01..72760000b 100644 --- a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt +++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/ValueParsersTest.kt @@ -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 { @@ -38,41 +39,41 @@ class ValueParsersTest { inflaterTest { module -> requiresActivity { activity -> startParse(module) - + // Hardcoded dimensions 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) - - 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 - } - + .isEqualTo(24) + + 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_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_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 = 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() } } @@ -92,11 +93,11 @@ class ValueParsersTest { } } } - + @Test fun `boolean parser test`() { inflaterTest { module -> - requiresActivity { activity -> + requiresActivity { startParse(module) assertThat(parseBoolean("true")).isTrue() assertThat(parseBoolean("false", def = true)).isFalse() @@ -110,16 +111,17 @@ 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)