Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,42 @@ public List<Location> deserialize(JsonElement json, Type typeOfT, JsonDeserializ
JsonObject jsonObject = jsonElement.getAsJsonObject();
Set<String> attributes = jsonObject.keySet();
if (!attributes.contains(LAT_ATTRIBUTE) || !attributes.contains(LON_ATTRIBUTE)) {
throw new JsonParseException("Unable to parse matchedPoint - 'lat' and 'lon' string values are expected");
throw new JsonParseException("Unable to parse matchedPoint - 'lat' and 'lon' values are expected");
}

String lat = jsonObject.get(LAT_ATTRIBUTE).getAsString();
String lon = jsonObject.get(LON_ATTRIBUTE).getAsString();
double lat = getCoordinate(jsonObject, LAT_ATTRIBUTE);
double lon = getCoordinate(jsonObject, LON_ATTRIBUTE);
Location matchPoint = getLocation(lat, lon);
locations.add(matchPoint);
}

return locations;
}

public Location getLocation(String lat, String lon) {
Location location = new Location(BuildConfig.LIBRARY_PACKAGE_NAME);
/**
* Reads a coordinate. The API sends these as numbers, but they used to be sent as strings,
* so both are accepted.
*/
private double getCoordinate(JsonObject jsonObject, String attribute) {
try {
location.setLatitude(Double.parseDouble(lat));
location.setLongitude(Double.parseDouble(lon));
return jsonObject.get(attribute).getAsDouble();
} catch (Exception e) {
throw new JsonParseException("Unable to parse matchedPoint - '" + attribute + "' is expected to be a number");
}
}

public Location getLocation(String lat, String lon) {
try {
return getLocation(Double.parseDouble(lat), Double.parseDouble(lon));
} catch (NumberFormatException e) {
throw new JsonParseException("Unable to parse matchedPoint - 'lat' and 'lon' string values expected to be parsable as doubles");
}
}

public Location getLocation(double lat, double lon) {
Location location = new Location(BuildConfig.LIBRARY_PACKAGE_NAME);
location.setLatitude(lat);
location.setLongitude(lon);
return location;
}
}
69 changes: 69 additions & 0 deletions sdk/src/main/java/com/adzerk/android/sdk/rest/Decision.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.google.gson.annotations.JsonAdapter;

import java.util.List;
import java.util.Map;

/**
* A Decision represents the ad that was selected to be served for a given {@link Placement}.
Expand Down Expand Up @@ -42,9 +43,27 @@ public class Decision {
// url endpoint that, using a GET, triggers the recording of the impression
String impressionUrl;

// height of the selected ad, if the creative supplies it
Integer height;

// width of the selected ad, if the creative supplies it
Integer width;

// custom metadata configured on the ad; only present if set
Map<String, Object> externalMetadata;

// ecpm partition of the matched impression; only present if set
String ecpmPartition;

// when multiple ads are selected for a non-multi-winner placement, the ads beyond the first
List<Decision> adChain;

@JsonAdapter(MatchedPointsDeserializer.class)
List<Location> matchedPoints;

// pricing details; only present when the Request sets includePricingData to true
PricingData pricing;

/**
* Returns id for the ad that was selected
* @return ad id
Expand Down Expand Up @@ -117,7 +136,57 @@ public List<Event> getEvents() {
return events;
}

/**
* Returns the height of the selected ad, or null if the creative does not supply one
* @return ad height
*/
public Integer getHeight() {
return height;
}

/**
* Returns the width of the selected ad, or null if the creative does not supply one
* @return ad width
*/
public Integer getWidth() {
return width;
}

/**
* Returns the custom metadata configured on the ad, or null if none is set
* @return map of custom metadata
*/
public Map<String, Object> getExternalMetadata() {
return externalMetadata;
}

/**
* Returns the ecpm partition of the matched impression, or null if none is set
* @return ecpm partition
*/
public String getEcpmPartition() {
return ecpmPartition;
}

/**
* Returns the additional ads beyond the first when multiple ads were selected for a
* non-multi-winner {@link Placement}, or null if there are none
* @return list of additional decisions
*/
public List<Decision> getAdChain() {
return adChain;
}

public List<Location> getMatchedPoints() {
return matchedPoints;
}

/**
* Returns the {@link PricingData} for the selected ad, or null if the {@link Request} did not
* set the includePricingData option
* @return pricing details
*/
public PricingData getPricing() {
return pricing;
}
}
103 changes: 103 additions & 0 deletions sdk/src/main/java/com/adzerk/android/sdk/rest/PricingData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.adzerk.android.sdk.rest;

import com.google.gson.annotations.SerializedName;

/**
* Pricing details for the ad that was selected for a {@link Placement}.
* <p>
* Only present when the ad {@link Request} sets the includePricingData option to true, and
* individual fields are only present when they apply to the matched impression.
*
* @see Decision
*/
public class PricingData {

// price of the impression
Float price;

// price the impression cleared at
Float clearPrice;

// only present when a bid modifier applied to the impression
Float modifiedPrice;

// only present when the flight has a targetROAS configured
Float optimizedPrice;

// multiplier applied to the value of the impression's events
Float eventMultiplier;

// revenue recorded for the impression
Float revenue;

// rate type of the flight that served the impression
Integer rateType;

// effective cost per thousand impressions
@SerializedName("eCPM")
Float eCPM;

/**
* Returns the price of the impression, or null if not present
* @return price
*/
public Float getPrice() {
return price;
}

/**
* Returns the price the impression cleared at, or null if not present
* @return clear price
*/
public Float getClearPrice() {
return clearPrice;
}

/**
* Returns the modified price, only present when a bid modifier applied to the impression
* @return modified price
*/
public Float getModifiedPrice() {
return modifiedPrice;
}

/**
* Returns the optimized price, only present when the flight has a targetROAS configured
* @return optimized price
*/
public Float getOptimizedPrice() {
return optimizedPrice;
}

/**
* Returns the multiplier applied to the value of the impression's events, or null if not present
* @return event multiplier
*/
public Float getEventMultiplier() {
return eventMultiplier;
}

/**
* Returns the revenue recorded for the impression, or null if not present
* @return revenue
*/
public Float getRevenue() {
return revenue;
}

/**
* Returns the rate type of the flight that served the impression, or null if not present
* @return rate type
*/
public Integer getRateType() {
return rateType;
}

/**
* Returns the effective cost per thousand impressions, or null if not present
* @return eCPM
*/
public Float getECPM() {
return eCPM;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ public void setUp() throws Exception {
gson = new GsonBuilder().create();
}

@Test
public void ShouldSucceed_WhenLatLonAreNumbers() {
MatchedPointsContainer result = gson.fromJson(JSON_VALID_NUMERIC_MATCHEDPOINTS, MatchedPointsContainer.class);

assertThat(result).isNotNull();
assertThat(result.matchedPoints).isNotNull().isNotEmpty().hasSize(3);
assertThat(result.matchedPoints.get(0).getLatitude()).isEqualTo(35.995063);
assertThat(result.matchedPoints.get(0).getLongitude()).isEqualTo(-78.908187);
assertThat(result.matchedPoints.get(1).getLatitude()).isEqualTo(40.689188);
assertThat(result.matchedPoints.get(1).getLongitude()).isEqualTo(-74.044562);
assertThat(result.matchedPoints.get(2).getLatitude()).isEqualTo(29.979188);
assertThat(result.matchedPoints.get(2).getLongitude()).isEqualTo(31.134188);
}

@Test
public void ShouldSucceed_WhenJsonIsValidMatchedPoints() {
MatchedPointsContainer result = gson.fromJson(JSON_VALID_MATCHEDPOINTS, MatchedPointsContainer.class);
Expand All @@ -55,7 +69,7 @@ public void ShouldThrow_WhenJsonIsNotArray() {
}

@Test(expected = JsonParseException.class)
public void ShouldThrow_WhenLatLonAreNotStrings() {
public void ShouldThrow_WhenLatLonAreNotNumeric() {
gson.fromJson(JSON_INVALID_2, MatchedPointsContainer.class);
}

Expand Down Expand Up @@ -94,12 +108,29 @@ public void ShouldThrow_WhenLatLonMissing() {
" };";


static String JSON_INVALID_2 =
static String JSON_VALID_NUMERIC_MATCHEDPOINTS =
" {\"matchedPoints\": [" +
" {" +
" \"lat\": 35.995063," +
" \"lon\": -78.908187" +
" }," +
" {" +
" \"lat\": 40.689188," +
" \"lon\": -74.044562" +
" }," +
" {" +
" \"lat\": 29.979188," +
" \"lon\": 31.134188" +
" }" +
" ]" +
" }";

static String JSON_INVALID_2 =
" {\"matchedPoints\": [" +
" {" +
" \"lat\": { \"degrees\": 35 }," +
" \"lon\": -78.908187" +
" }" +
" ]" +
" }";

Expand Down
Loading
Loading