Skip to content

Commit

Permalink
Merge pull request #14 from Optable/cache-targeting
Browse files Browse the repository at this point in the history
targetingFromCache() and targetingClearCache() APIs
  • Loading branch information
bmilekic authored Nov 10, 2020
2 parents c999319 + e00bd86 commit 8f70220
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.google.android.gms.ads.doubleclick.PublisherAdView;

import java.util.HashMap;
import java.util.List;

import co.optable.android_sdk.OptableSDK;
import co.optable.demoappjava.MainActivity;
Expand All @@ -30,6 +31,7 @@ public View onCreateView(@NonNull LayoutInflater inflater,
mPublisherAdView = root.findViewById(R.id.publisherAdView);
targetingDataView = root.findViewById(R.id.targetingDataView);

// loadAdButton loads targeting data and then the GAM banner:
Button btn = root.findViewById(R.id.loadAdButton);
btn.setOnClickListener(view -> {
targetingDataView.setText("");
Expand All @@ -51,27 +53,60 @@ public View onCreateView(@NonNull LayoutInflater inflater,

targetingDataView.setText(msg.toString());
mPublisherAdView.loadAd(adRequest.build());
witness();
});
});

HashMap<String, String> eventProperties = new HashMap<String, String>();
eventProperties.put("exampleKey", "exampleValue");

MainActivity.OPTABLE
.witness("GAMBannerFragment.loadAdButtonClicked", eventProperties)
.observe(getViewLifecycleOwner(), result -> {
final StringBuilder msg = new StringBuilder();
msg.append(targetingDataView.getText().toString());

if (result.getStatus() == OptableSDK.Status.SUCCESS) {
msg.append("\n\nSuccess calling witness API to log loadAdButtonClicked event.\n\n");
} else {
msg.append("\n\nOptableSDK Error: " + result.getMessage() + "\n\n");
}
// loadAdButton2 loads targeting data from cache, and then the GAM banner:
btn = root.findViewById(R.id.loadAdButton2);
btn.setOnClickListener(view -> {
targetingDataView.setText("");
PublisherAdRequest.Builder adRequest = new PublisherAdRequest.Builder();
final StringBuilder msg = new StringBuilder();
HashMap<String, List<String>> data = MainActivity.OPTABLE.targetingFromCache();

if (data != null) {
msg.append("Loading GAM ad with cached targeting data:\n\n");
data.forEach((key, values) -> {
adRequest.addCustomTargeting(key, values);
msg.append(key.toString() + " = " + values.toString());
});
} else {
msg.append("Targeting data cache empty.");
}

targetingDataView.setText(msg.toString());
mPublisherAdView.loadAd(adRequest.build());
witness();
});

targetingDataView.setText(msg.toString());
});
// loadAdButton3 clears targeting data cache:
btn = root.findViewById(R.id.loadAdButton3);
btn.setOnClickListener(view -> {
targetingDataView.setText("Clearing targeting data cache.\n\n");
MainActivity.OPTABLE.targetingClearCache();
});

return root;
}

private void witness() {
HashMap<String, String> eventProperties = new HashMap<String, String>();
eventProperties.put("exampleKey", "exampleValue");

MainActivity.OPTABLE
.witness("GAMBannerFragment.loadAdButtonClicked", eventProperties)
.observe(getViewLifecycleOwner(), result -> {
final StringBuilder msg = new StringBuilder();
msg.append(targetingDataView.getText().toString());

if (result.getStatus() == OptableSDK.Status.SUCCESS) {
msg.append("\n\nSuccess calling witness API to log loadAdButtonClicked event.\n\n");
} else {
msg.append("\n\nOptableSDK Error: " + result.getMessage() + "\n\n");
}

targetingDataView.setText(msg.toString());
});
}
}
31 changes: 29 additions & 2 deletions DemoApp/DemoAppJava/app/src/main/res/layout/fragment_gambanner.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,37 @@

<Button
android:id="@+id/loadAdButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="Load Banner"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.054"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/loadAdButton2"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="Cached Banner"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/loadAdButton3"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="Clear Cache"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.945"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ class GAMBannerFragment : Fragment() {
targetingDataView = root.findViewById(R.id.targetingDataView)
targetingDataView.setText("")

// loadAdButton loads targeting data and then the GAM banner:
var btn = root.findViewById(R.id.loadAdButton) as Button
btn.setOnClickListener {
MainActivity.OPTABLE!!.targeting().observe(viewLifecycleOwner, Observer { result ->
var msg = targetingDataView.text.toString()
var msg = ""
var adRequest = PublisherAdRequest.Builder()

if (result.status == OptableSDK.Status.SUCCESS) {
Expand All @@ -51,14 +52,49 @@ class GAMBannerFragment : Fragment() {

targetingDataView.setText(msg)
mPublisherAdView.loadAd(adRequest.build())
witness()
})
}

MainActivity.OPTABLE!!
.witness(
"GAMBannerFragment.loadAdButtonClicked",
hashMapOf("exampleKey" to "exampleValue")
)
.observe(viewLifecycleOwner, Observer { result ->
// loadAdButton2 loads targeting data from cache, and then the GAM banner:
btn = root.findViewById(R.id.loadAdButton2) as Button
btn.setOnClickListener {
var msg = ""
var adRequest = PublisherAdRequest.Builder()
var data = MainActivity.OPTABLE!!.targetingFromCache()

if (data != null) {
msg += "Loading GAM ad with cached targeting data:\n\n"
data!!.forEach { (key, values) ->
adRequest.addCustomTargeting(key, values)
msg += "${key} = ${values}\n"
}
} else {
msg += "Targeting data cache empty."
}

targetingDataView.setText(msg)
mPublisherAdView.loadAd(adRequest.build())
witness()
}

// loadAdButton3 clears targeting data cache:
btn = root.findViewById(R.id.loadAdButton3) as Button
btn.setOnClickListener {
targetingDataView.setText("Clearing targeting data cache.\n\n")
MainActivity.OPTABLE!!.targetingClearCache()
}

return root
}

private fun witness() {
MainActivity.OPTABLE!!
.witness(
"GAMBannerFragment.loadAdButtonClicked",
hashMapOf("exampleKey" to "exampleValue")
)
.observe(viewLifecycleOwner, Observer { result ->
var msg = targetingDataView.text.toString()
if (result.status == OptableSDK.Status.SUCCESS) {
msg += "\n\nSuccess calling witness API to log loadAdButtonClicked event.\n\n"
Expand All @@ -67,9 +103,6 @@ class GAMBannerFragment : Fragment() {
}
targetingDataView.setText(msg)
})
}

return root
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,37 @@

<Button
android:id="@+id/loadAdButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="Load Banner"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.054"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/loadAdButton2"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="Cached Banner"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/loadAdButton3"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:text="Clear Cache"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.945"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,48 @@ MainActivity.OPTABLE

On success, the resulting key values are typically sent as part of a subsequent ad call. Therefore we recommend that you either call `targeting()` before each ad call, or in parallel periodically, caching the resulting key values which you then provide in ad calls.

#### Caching Targeting Data

The `targeting` API will automatically cache resulting key value data in client storage on success. You can subsequently retrieve the cached key value data as follows:

##### Kotlin

```kotlin
var cachedTargetingData = MainActivity.OPTABLE!!.targetingFromCache()
if (cachedTargetingData != null) {
cachedTargetingData!!.forEach { (key, values) ->
// key is a String and values is a List<String> ...
}
}
```

##### Java

```java
HashMap<String, List<String>> cachedTargetingData = MainActivity.OPTABLE.targetingFromCache();
if (cachedTargetingData != null) {
cachedTargetingData.forEach((key, values) -> {
// key is a String and values is a List<String> ...
});
}
```

You can also clear the locally cached targeting data:

##### Kotlin

```kotlin
MainActivity.OPTABLE!!.targetingClearCache()
```

##### Java

```java
MainActivity.OPTABLE.targetingClearCache();
```

Note that both `targetingFromCache()` and `targetingClearCache()` are synchronous.

### Witness API

To send real-time event data from the user's device to the sandbox for eventual audience assembly, you can call the witness API as follows:
Expand Down
2 changes: 1 addition & 1 deletion android_sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.7.0'
implementation 'com.squareup.retrofit2:converter-gson:2.7.0'
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.google.code.gson:gson:2.8.6'
implementation "androidx.preference:preference-ktx:1.1.1"
implementation 'com.google.android.gms:play-services-ads-lite:11.8.0'
implementation 'androidx.core:core-ktx:1.3.1'
Expand Down
13 changes: 12 additions & 1 deletion android_sdk/src/main/java/co/optable/android_sdk/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
package co.optable.android_sdk

import android.util.Base64

class Config(val host: String, val app: String, val insecure: Boolean = false) {

fun edgeBaseURL(): String {
Expand All @@ -15,7 +17,16 @@ class Config(val host: String, val app: String, val insecure: Boolean = false) {
}

fun passportKey(): String {
return "OPTABLE_" + this.host + "/" + this.app
return key("PASS")
}

fun targetingKey(): String {
return key("TGT")
}

private fun key(kind: String): String {
val sfx = this.host + "/" + this.app
return "OPTABLE_" + kind + "_" + Base64.encodeToString(sfx.toByteArray(), 0)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class OptableSDK @JvmOverloads constructor(context: Context, host: String, app:
val response = client.Targeting()
when (response) {
is EdgeResponse.Success -> {
client.TargetingSetCache(response.body)
liveData.postValue(Response.success(response.body))
}
is EdgeResponse.ApiError -> {
Expand All @@ -217,6 +218,14 @@ class OptableSDK @JvmOverloads constructor(context: Context, host: String, app:
return liveData
}

fun targetingFromCache(): OptableTargetingResponse? {
return this.client.TargetingFromCache()
}

fun targetingClearCache() {
this.client.TargetingClearCache()
}

/*
* witness(event, properties) calls the Optable Sandbox "witness" API in order to log a
* specified 'event' (e.g., "app.screenView", "ui.buttonPressed"), with the specified keyvalue
Expand Down
12 changes: 12 additions & 0 deletions android_sdk/src/main/java/co/optable/android_sdk/core/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ class Client(private val config: Config, private val context: Context) {
return edgeService!!.Witness(this.config.app, evtBody)
}

fun TargetingSetCache(keyvalues: OptableTargetingResponse) {
storage.setTargeting(keyvalues)
}

fun TargetingFromCache(): OptableTargetingResponse? {
return storage.getTargeting()
}

fun TargetingClearCache() {
storage.clearTargeting()
}

fun hasGAID(): Boolean {
return ((gaid != null) && (gaidLAT == false) && !TextUtils.isEmpty(gaid!!))
}
Expand Down
Loading

0 comments on commit 8f70220

Please sign in to comment.