From 4574bf0ab73cfbf427258985315e808310a6a5e5 Mon Sep 17 00:00:00 2001 From: OleS Date: Wed, 15 Oct 2025 10:18:08 +0300 Subject: [PATCH 1/4] Fix: bra body measurements - suggested size issue fixed --- .../local/BodyProfileRecommendedSizeParams.kt | 35 ++++++++++++++ .../data/parsers/UserBodyProfileJsonParser.kt | 7 ++- .../android/data/remote/UserBodyProfile.kt | 3 ++ .../virtusize/android/network/VirtusizeApi.kt | 48 +++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt b/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt index 8e532a9e..ba37f84c 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt @@ -173,10 +173,45 @@ internal data class BodyProfileRecommendedSizeParams( ) }.orEmpty(), ) + .plus( + createBraSizeParams(), + ) // Convert all keys in the map to snake_case .mapKeys { (key, _) -> toSnakeCase(key) } } + /** + * Creates the map that represents the bra size data in body_data format + * Matches iOS implementation: multiplies band value by 10, prefixes keys with "bra_" + */ + private fun createBraSizeParams(): Map { + val braSize = userBodyProfile.braSize ?: return emptyMap() + val braSizeParams = mutableMapOf() + + for ((key, value) in braSize) { + val snakeKey = toSnakeCase(key) + if (snakeKey == "band") { + // Multiply band value by 10, matching iOS implementation + val bandValue = when (value) { + is Int -> value * 10 + is Number -> value.toInt() * 10 + else -> value + } + braSizeParams["bra_$snakeKey"] = mutableMapOf( + PARAM_BODY_MEASUREMENT_VALUE to bandValue, + PARAM_BODY_MEASUREMENT_PREDICTED to true, + ) + } else { + braSizeParams["bra_$snakeKey"] = mutableMapOf( + PARAM_BODY_MEASUREMENT_VALUE to value, + PARAM_BODY_MEASUREMENT_PREDICTED to true, + ) + } + } + + return braSizeParams + } + /** * Creates the map that represents the store product size info */ diff --git a/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt b/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt index 9a0a7273..5e37df1b 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt @@ -12,16 +12,20 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser { val weight = json.optString(FIELD_WEIGHT) var bodyData = setOf() var footwearData = mapOf() + var braSize: Map? = null json.optJSONObject(FIELD_BODY_DATA)?.let { bodyDataJsonObject -> bodyData = JsonUtils.jsonObjectToMeasurements(bodyDataJsonObject) } json.optJSONObject(FIELD_FOOTWEAR_DATA)?.let { footwearDataJsonObject -> footwearData = JsonUtils.jsonObjectToMap(footwearDataJsonObject) } + json.optJSONObject(FIELD_BRA_SIZE)?.let { braSizeJsonObject -> + braSize = JsonUtils.jsonObjectToMap(braSizeJsonObject) + } if (age == 0 || height == 0 || weight.isBlank() || bodyData.isEmpty()) { return null } - return UserBodyProfile(gender, age, height, weight, bodyData, footwearData) + return UserBodyProfile(gender, age, height, weight, bodyData, footwearData, braSize) } companion object { @@ -31,5 +35,6 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser { private const val FIELD_WEIGHT = "weight" private const val FIELD_BODY_DATA = "bodyData" private const val FIELD_FOOTWEAR_DATA = "footwearData" + private const val FIELD_BRA_SIZE = "braSize" } } diff --git a/virtusize-core/src/main/java/com/virtusize/android/data/remote/UserBodyProfile.kt b/virtusize-core/src/main/java/com/virtusize/android/data/remote/UserBodyProfile.kt index d7121550..b0ed3ead 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/data/remote/UserBodyProfile.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/data/remote/UserBodyProfile.kt @@ -7,6 +7,8 @@ package com.virtusize.android.data.remote * @param height the user's height * @param weight the user's weight * @param bodyData the user's body measurement data, such as hip, bust, waist and so on. + * @param footwearData the user's footwear data, such as shoe size and width + * @param braSize the user's bra size information (country, cup, band) */ data class UserBodyProfile( val gender: String, @@ -15,4 +17,5 @@ data class UserBodyProfile( val weight: String, val bodyData: Set, val footwearData: Map, + val braSize: Map? = null, ) diff --git a/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt b/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt index 8f0c50c7..eaae9e98 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt @@ -27,6 +27,7 @@ import com.virtusize.android.data.remote.UserBodyProfile enum class HttpMethod { GET, POST, + PUT, DELETE, } @@ -407,6 +408,53 @@ object VirtusizeApi { return ApiRequest(url, HttpMethod.GET, authorization = true) } + /** + * Gets a API request for updating the user body profile + * @param userBodyProfile the user body profile to update + * @see ApiRequest + */ + fun updateUserBodyProfile(userBodyProfile: UserBodyProfile): ApiRequest { + val url = + Uri.parse( + environment.defaultApiUrl() + VirtusizeEndpoint.UserBodyMeasurements.path, + ) + .buildUpon() + .build() + .toString() + val params = mutableMapOf() + + // Add bra_size if available + userBodyProfile.braSize?.let { + params["bra_size"] = it + } + + // Add body_data + val bodyDataMap = mutableMapOf() + userBodyProfile.bodyData.forEach { measurement -> + bodyDataMap[measurement.name] = measurement.millimeter + } + params["body_data"] = bodyDataMap + + // Add footwear_data + params["footwear_data"] = userBodyProfile.footwearData + + // Add concern_areas (default to zeros) + params["concern_areas"] = mapOf( + "shoulder" to 0, + "bust" to 0, + "waist" to 0, + "hip" to 0 + ) + + // Add other profile fields + params["gender"] = userBodyProfile.gender + params["age"] = userBodyProfile.age + params["height"] = userBodyProfile.height + params["weight"] = userBodyProfile.weight + + return ApiRequest(url, HttpMethod.PUT, params, authorization = true) + } + /** * Gets a API request for getting the recommended size based on the user's body profile * @param productTypes the list of available [ProductType] From 117877e1eb4f9fe1ae18d56eda772b56cffd691c Mon Sep 17 00:00:00 2001 From: OleS Date: Wed, 15 Oct 2025 10:25:03 +0300 Subject: [PATCH 2/4] Increment build number --- CHANGELOG.md | 3 +++ README-COMPOSE.md | 4 ++-- README-JP.md | 4 ++-- README.md | 4 ++-- .../main/java/com/virtusize/android/constants/Constants.kt | 2 +- gradle/libs.versions.toml | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c59784be..6e729872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +#### Fixes +- Fix: bra body measurements - suggested size issue fixed + ### 2.12.6 - Feature: Add Android 15+ support a memory page size of 16KB. diff --git a/README-COMPOSE.md b/README-COMPOSE.md index 7fd3440e..3db0c8b0 100644 --- a/README-COMPOSE.md +++ b/README-COMPOSE.md @@ -74,7 +74,7 @@ In your app `build.gradle` file, add the following dependencies: ```groovy dependencies { - implementation 'com.virtusize.android:virtusize:2.12.6' + implementation 'com.virtusize.android:virtusize:2.12.7' } ``` @@ -82,7 +82,7 @@ In your app `build.gradle` file, add the following dependencies: ```kotlin dependencies { - implementation("com.virtusize.android:virtusize:2.12.6") + implementation("com.virtusize.android:virtusize:2.12.7") } ``` diff --git a/README-JP.md b/README-JP.md index 9e09a57e..de4e5ecb 100644 --- a/README-JP.md +++ b/README-JP.md @@ -75,7 +75,7 @@ appの`build.gradle`ファイルに下記のdependencyを追加 ```groovy dependencies { - implementation 'com.virtusize.android:virtusize:2.12.6' + implementation 'com.virtusize.android:virtusize:2.12.7' } ``` @@ -83,7 +83,7 @@ appの`build.gradle`ファイルに下記のdependencyを追加 ```kotlin dependencies { - implementation("com.virtusize.android:virtusize:2.12.6") + implementation("com.virtusize.android:virtusize:2.12.7") } ``` diff --git a/README.md b/README.md index 50f78230..1ac5dc19 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ In your app `build.gradle` file, add the following dependencies: ```groovy dependencies { - implementation 'com.virtusize.android:virtusize:2.12.6' + implementation 'com.virtusize.android:virtusize:2.12.7' } ``` @@ -90,7 +90,7 @@ In your app `build.gradle` file, add the following dependencies: ```kotlin dependencies { - implementation("com.virtusize.android:virtusize:2.12.6") + implementation("com.virtusize.android:virtusize:2.12.7") } ``` diff --git a/buildSrc/src/main/java/com/virtusize/android/constants/Constants.kt b/buildSrc/src/main/java/com/virtusize/android/constants/Constants.kt index b2b87eea..f6054687 100644 --- a/buildSrc/src/main/java/com/virtusize/android/constants/Constants.kt +++ b/buildSrc/src/main/java/com/virtusize/android/constants/Constants.kt @@ -6,6 +6,6 @@ object Constants { const val TARGET_SDK = 35 // Update versionName when publishing a new release - const val VERSION_NAME = "2.12.6" + const val VERSION_NAME = "2.12.7" const val GROUP_ID = "com.virtusize.android" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91e4138b..750bce92 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ material = "1.12.0" nextPublish = "1.1.0" robolectric = "4.13" truth = "1.4.4" -virtusize = "2.12.6" +virtusize = "2.12.7" virtusizeAuth = "1.1.1" browser = "1.8.0" From 78e5a9b7c4324c2b906e4618536c08f8ca0d8061 Mon Sep 17 00:00:00 2001 From: OleS Date: Wed, 15 Oct 2025 12:05:59 +0300 Subject: [PATCH 3/4] Unit-Test updates --- .../data/parsers/UserBodyProfileJsonParser.kt | 3 +- .../parsers/UserBodyProfileJsonParserTest.kt | 28 ++++++ .../android/fixtures/TestFixtures.kt | 94 +++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt b/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt index 5e37df1b..cbb2535a 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParser.kt @@ -20,7 +20,8 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser { footwearData = JsonUtils.jsonObjectToMap(footwearDataJsonObject) } json.optJSONObject(FIELD_BRA_SIZE)?.let { braSizeJsonObject -> - braSize = JsonUtils.jsonObjectToMap(braSizeJsonObject) + val braSizeMap = JsonUtils.jsonObjectToMap(braSizeJsonObject) + braSize = if (braSizeMap.isEmpty()) null else braSizeMap } if (age == 0 || height == 0 || weight.isBlank() || bodyData.isEmpty()) { return null diff --git a/virtusize-core/src/test/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParserTest.kt b/virtusize-core/src/test/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParserTest.kt index c88f9275..0bf5171d 100644 --- a/virtusize-core/src/test/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParserTest.kt +++ b/virtusize-core/src/test/java/com/virtusize/android/data/parsers/UserBodyProfileJsonParserTest.kt @@ -26,4 +26,32 @@ class UserBodyProfileJsonParserTest { assertThat(actualUserBodyProfile).isNull() } + + @Test + fun test_parseValidUserBodyResponseWithBraSize_returnExpectedUserBodyProfile() { + val actualUserBodyProfile = + UserBodyProfileJsonParser().parse(TestFixtures.USER_BODY_WITH_BRA_SIZE_JSONObject) + assertThat(actualUserBodyProfile).isEqualTo(TestFixtures.userBodyProfileWithBraSize) + } + + @Test + fun test_parseUserBodyWithBraSize_braSizeIsCorrectlyParsed() { + val actualUserBodyProfile = + UserBodyProfileJsonParser().parse(TestFixtures.USER_BODY_WITH_BRA_SIZE_JSONObject) + + assertThat(actualUserBodyProfile).isNotNull() + assertThat(actualUserBodyProfile?.braSize).isNotNull() + assertThat(actualUserBodyProfile?.braSize).containsEntry("country", "US") + assertThat(actualUserBodyProfile?.braSize).containsEntry("cup", "B") + assertThat(actualUserBodyProfile?.braSize).containsEntry("band", 34) + } + + @Test + fun test_parseUserBodyWithoutBraSize_braSizeIsNull() { + val actualUserBodyProfile = + UserBodyProfileJsonParser().parse(TestFixtures.USER_BODY_JSONObject) + + assertThat(actualUserBodyProfile).isNotNull() + assertThat(actualUserBodyProfile?.braSize).isNull() + } } diff --git a/virtusize-core/src/test/java/com/virtusize/android/fixtures/TestFixtures.kt b/virtusize-core/src/test/java/com/virtusize/android/fixtures/TestFixtures.kt index 81afb03f..46b99fc2 100644 --- a/virtusize-core/src/test/java/com/virtusize/android/fixtures/TestFixtures.kt +++ b/virtusize-core/src/test/java/com/virtusize/android/fixtures/TestFixtures.kt @@ -155,5 +155,99 @@ internal object TestFixtures { "brand" to "Virtusize", "footWidth" to "regular", ), + null, + ) + + val USER_BODY_WITH_BRA_SIZE_JSONObject = + JSONObject( + """ + { + "wardrobe": "1234567", + "gender": "female", + "age": 32, + "height": 1630, + "weight": "50.00", + "braSize": { + "country": "US", + "cup": "B", + "band": 34 + }, + "concernAreas": {}, + "bodyData": { + "hip": 830, + "bust": 755, + "neck": 300, + "rise": 215, + "bicep": 220, + "thigh": 480, + "waist": 630, + "inseam": 700, + "sleeve": 720, + "shoulder": 370, + "hipWidth": 300, + "bustWidth": 245, + "hipHeight": 750, + "headHeight": 215, + "kneeHeight": 395, + "waistWidth": 225, + "waistHeight": 920, + "armpitHeight": 1130, + "sleeveLength": 520, + "shoulderWidth": 340, + "shoulderHeight": 1240 + }, + "footwearData": { + "toeShape":"greek", + "size":"30.5", + "type":"sneakers", + "brand":"Virtusize", + "footWidth":"regular" + } + } + """.trimIndent(), + ) + + val userBodyProfileWithBraSize = + UserBodyProfile( + "female", + 32, + 1630, + "50.00", + mutableSetOf( + Measurement("hip", 830), + Measurement("hip", 830), + Measurement("bust", 755), + Measurement("neck", 300), + Measurement("rise", 215), + Measurement("bicep", 220), + Measurement("thigh", 480), + Measurement("waist", 630), + Measurement("inseam", 700), + Measurement("sleeve", 720), + Measurement("shoulder", 370), + Measurement("hipWidth", 300), + Measurement("bustWidth", 245), + Measurement("hipHeight", 750), + Measurement("headHeight", 215), + Measurement("kneeHeight", 395), + Measurement("waistWidth", 225), + Measurement("waistHeight", 920), + Measurement("armpitHeight", 1130), + Measurement("sleeveLength", 520), + Measurement("shoulderWidth", 340), + Measurement("shoulderHeight", 1240), + ), + mapOf( + "toeShape" to "greek", + "size" to "30.5", + "type" to "sneakers", + "brand" to "Virtusize", + "footWidth" to "regular", + ), + mapOf( + "country" to "US", + "cup" to "B", + "band" to 34, + ), ) } From 0b8f3b27a0521870410b758d89dc4b4981208064 Mon Sep 17 00:00:00 2001 From: OleS Date: Wed, 15 Oct 2025 12:14:16 +0300 Subject: [PATCH 4/4] Fix linter issues --- .../local/BodyProfileRecommendedSizeParams.kt | 29 ++++++++++--------- .../virtusize/android/network/VirtusizeApi.kt | 13 +++++---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt b/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt index ba37f84c..df16dc5f 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/data/local/BodyProfileRecommendedSizeParams.kt @@ -192,20 +192,23 @@ internal data class BodyProfileRecommendedSizeParams( val snakeKey = toSnakeCase(key) if (snakeKey == "band") { // Multiply band value by 10, matching iOS implementation - val bandValue = when (value) { - is Int -> value * 10 - is Number -> value.toInt() * 10 - else -> value - } - braSizeParams["bra_$snakeKey"] = mutableMapOf( - PARAM_BODY_MEASUREMENT_VALUE to bandValue, - PARAM_BODY_MEASUREMENT_PREDICTED to true, - ) + val bandValue = + when (value) { + is Int -> value * 10 + is Number -> value.toInt() * 10 + else -> value + } + braSizeParams["bra_$snakeKey"] = + mutableMapOf( + PARAM_BODY_MEASUREMENT_VALUE to bandValue, + PARAM_BODY_MEASUREMENT_PREDICTED to true, + ) } else { - braSizeParams["bra_$snakeKey"] = mutableMapOf( - PARAM_BODY_MEASUREMENT_VALUE to value, - PARAM_BODY_MEASUREMENT_PREDICTED to true, - ) + braSizeParams["bra_$snakeKey"] = + mutableMapOf( + PARAM_BODY_MEASUREMENT_VALUE to value, + PARAM_BODY_MEASUREMENT_PREDICTED to true, + ) } } diff --git a/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt b/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt index eaae9e98..ca2384d8 100644 --- a/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt +++ b/virtusize-core/src/main/java/com/virtusize/android/network/VirtusizeApi.kt @@ -439,12 +439,13 @@ object VirtusizeApi { params["footwear_data"] = userBodyProfile.footwearData // Add concern_areas (default to zeros) - params["concern_areas"] = mapOf( - "shoulder" to 0, - "bust" to 0, - "waist" to 0, - "hip" to 0 - ) + params["concern_areas"] = + mapOf( + "shoulder" to 0, + "bust" to 0, + "waist" to 0, + "hip" to 0, + ) // Add other profile fields params["gender"] = userBodyProfile.gender