Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
4 changes: 2 additions & 2 deletions README-COMPOSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ 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'
}
```

- Kotlin (build.gradle.kts)

```kotlin
dependencies {
implementation("com.virtusize.android:virtusize:2.12.6")
implementation("com.virtusize.android:virtusize:2.12.7")
}
```

Expand Down
4 changes: 2 additions & 2 deletions README-JP.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ appの`build.gradle`ファイルに下記のdependencyを追加

```groovy
dependencies {
implementation 'com.virtusize.android:virtusize:2.12.6'
implementation 'com.virtusize.android:virtusize:2.12.7'
}
```

- Kotlin (build.gradle.kts)

```kotlin
dependencies {
implementation("com.virtusize.android:virtusize:2.12.6")
implementation("com.virtusize.android:virtusize:2.12.7")
}
```

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ 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'
}
```

- Kotlin (build.gradle.kts)

```kotlin
dependencies {
implementation("com.virtusize.android:virtusize:2.12.6")
implementation("com.virtusize.android:virtusize:2.12.7")
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,48 @@ 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<String, Any> {
val braSize = userBodyProfile.braSize ?: return emptyMap()
val braSizeParams = mutableMapOf<String, Any>()

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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser<UserBodyProfile> {
val weight = json.optString(FIELD_WEIGHT)
var bodyData = setOf<Measurement>()
var footwearData = mapOf<String, Any>()
var braSize: Map<String, Any>? = 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 ->
val braSizeMap = JsonUtils.jsonObjectToMap(braSizeJsonObject)
braSize = if (braSizeMap.isEmpty()) null else braSizeMap
}
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 {
Expand All @@ -31,5 +36,6 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser<UserBodyProfile> {
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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -15,4 +17,5 @@ data class UserBodyProfile(
val weight: String,
val bodyData: Set<Measurement>,
val footwearData: Map<String, Any>,
val braSize: Map<String, Any>? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.virtusize.android.data.remote.UserBodyProfile
enum class HttpMethod {
GET,
POST,
PUT,
DELETE,
}

Expand Down Expand Up @@ -407,6 +408,54 @@ 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<String, Any>()

// Add bra_size if available
userBodyProfile.braSize?.let {
params["bra_size"] = it
}

// Add body_data
val bodyDataMap = mutableMapOf<String, Int>()
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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
)
}