Skip to content

Commit f56a990

Browse files
feat: introduce Optable demo cases with Prebid
1 parent 3ed5781 commit f56a990

20 files changed

Lines changed: 682 additions & 57 deletions

File tree

DemoApp/DemoAppJava/app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ dependencies {
4646
// Google Mobile Ads
4747
implementation 'com.google.android.gms:play-services-ads:24.6.0'
4848

49+
// Prebid Ads
50+
implementation "org.prebid:prebid-mobile-sdk:3.0.2"
51+
4952
// Base Android
5053
implementation "org.jetbrains.kotlin:kotlin-stdlib:2.0.21"
5154
implementation 'com.android.support:multidex:1.0.3'

DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/MainActivity.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package co.optable.demoappjava;
22

33
import android.os.Bundle;
4+
import android.util.Log;
45
import androidx.appcompat.app.AppCompatActivity;
56
import androidx.navigation.NavController;
67
import androidx.navigation.Navigation;
78
import androidx.navigation.ui.AppBarConfiguration;
89
import androidx.navigation.ui.NavigationUI;
910
import co.optable.android_sdk.OptableSDK;
11+
import com.google.android.gms.ads.MobileAds;
1012
import com.google.android.material.bottomnavigation.BottomNavigationView;
13+
import org.prebid.mobile.PrebidMobile;
14+
import org.prebid.mobile.api.data.InitializationStatus;
1115

1216
public class MainActivity extends AppCompatActivity {
1317

18+
private static final String TAG = "MainActivity";
19+
1420
public static OptableSDK OPTABLE;
1521

1622
@Override
@@ -20,13 +26,35 @@ protected void onCreate(Bundle savedInstanceState) {
2026

2127
MainActivity.OPTABLE = new OptableSDK(this.getApplicationContext(), "sandbox.optable.co", "ios-sdk-demo");
2228

29+
initGoogleAds();
30+
initPrebidSdk();
2331
initUi();
2432
}
2533

34+
private void initGoogleAds() {
35+
MobileAds.initialize(this, initializationStatus -> {
36+
});
37+
}
38+
39+
private void initPrebidSdk() {
40+
PrebidMobile.setPrebidServerAccountId("0689a263-318d-448b-a3d4-b02e8a709d9d");
41+
PrebidMobile.initializeSdk(getApplicationContext(), "https://prebid-server-test-j.prebid.org/openrtb2/auction", status -> {
42+
if (status == InitializationStatus.SUCCEEDED) {
43+
Log.d(TAG, "SDK initialized successfully!");
44+
} else {
45+
Log.e(TAG, "SDK initialization error: " + status.getDescription());
46+
}
47+
});
48+
}
49+
2650
private void initUi() {
2751
BottomNavigationView navView = findViewById(R.id.nav_view);
2852
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
29-
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.navigation_identify, R.id.navigation_gambanner).build();
53+
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
54+
R.id.navigation_identify,
55+
R.id.navigation_gambanner,
56+
R.id.navigation_prebid
57+
).build();
3058
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
3159
NavigationUI.setupWithNavController(navView, navController);
3260
}

DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/GAMBanner/GAMBannerFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
3434

3535
private void initUi(View root) {
3636
mAdView = root.findViewById(R.id.publisherAdView);
37-
statusTextView = root.findViewById(R.id.targetingDataView);
37+
statusTextView = root.findViewById(R.id.statusTextView);
3838

3939
root.findViewById(R.id.btnLoadBanner).setOnClickListener(view -> onClickLoadAd());
4040
root.findViewById(R.id.btnCachedBanner).setOnClickListener(view -> onClickCachedBanner());
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package co.optable.demoappjava.ui;
2+
3+
import android.os.Bundle;
4+
import android.view.LayoutInflater;
5+
import android.view.View;
6+
import android.view.ViewGroup;
7+
import android.widget.TextView;
8+
import androidx.annotation.NonNull;
9+
import androidx.annotation.Nullable;
10+
import androidx.fragment.app.Fragment;
11+
import co.optable.android_sdk.OptableSDK;
12+
import co.optable.demoappjava.MainActivity;
13+
import co.optable.demoappjava.R;
14+
import com.google.android.gms.ads.AdListener;
15+
import com.google.android.gms.ads.LoadAdError;
16+
import com.google.android.gms.ads.admanager.AdManagerAdRequest;
17+
import com.google.android.gms.ads.admanager.AdManagerAdView;
18+
import org.jetbrains.annotations.NotNull;
19+
import org.prebid.mobile.BannerAdUnit;
20+
import org.prebid.mobile.ExternalUserId;
21+
import org.prebid.mobile.TargetingParams;
22+
23+
import java.util.*;
24+
25+
public class PrebidBannerFragment extends Fragment {
26+
27+
private static final String GAM_AD_UNIT_ID = "/21808260008/prebid_demo_app_original_api_banner";
28+
private static final String PREBID_CONFIG_ID = "prebid-demo-banner-320-50";
29+
private static final int WIDTH = 320;
30+
private static final int HEIGHT = 50;
31+
32+
private AdManagerAdView adView;
33+
private BannerAdUnit prebidAdUnit;
34+
35+
private ViewGroup adContainer;
36+
private TextView statusTextView;
37+
38+
@Override
39+
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
40+
View root = inflater.inflate(R.layout.fragment_prebid, container, false);
41+
initUi(root);
42+
return root;
43+
}
44+
45+
private void initUi(View root) {
46+
statusTextView = root.findViewById(R.id.statusTextView);
47+
adContainer = root.findViewById(R.id.adContainer);
48+
49+
root.findViewById(R.id.btnLoadBanner).setOnClickListener(view -> onClickLoadAd());
50+
root.findViewById(R.id.btnCachedBanner).setOnClickListener(view -> onClickCachedBanner());
51+
root.findViewById(R.id.btnClearCache).setOnClickListener(view -> onClickClearCache());
52+
}
53+
54+
/**
55+
* Loads targeting data and then the GAM banner
56+
*/
57+
private void onClickLoadAd() {
58+
statusTextView.setText("");
59+
60+
MainActivity.OPTABLE
61+
.targeting()
62+
.observe(getViewLifecycleOwner(), result -> {
63+
AdManagerAdRequest.Builder adRequestBuilder = new AdManagerAdRequest.Builder();
64+
65+
if (result.getStatus() == OptableSDK.Status.SUCCESS) {
66+
HashMap<String, List<String>> data = result.getData();
67+
changeStatusText("Loaded Optable targeting data", data);
68+
69+
if (data != null) {
70+
for (String key : data.keySet()) {
71+
List<String> values = data.get(key);
72+
if (values == null) continue;
73+
adRequestBuilder.addCustomTargeting(key, values);
74+
}
75+
}
76+
} else {
77+
changeStatusText("Error loading Optable targeting data: " + result.getMessage(), null);
78+
}
79+
80+
loadPrebidAd(adRequestBuilder, result.getData());
81+
profile();
82+
witness();
83+
});
84+
}
85+
86+
private void loadPrebidAd(AdManagerAdRequest.Builder adRequestBuilder, @Nullable HashMap<String, List<String>> optableTargeting) {
87+
prebidAdUnit = new BannerAdUnit(PREBID_CONFIG_ID, WIDTH, HEIGHT);
88+
applyOptableToPrebid(optableTargeting);
89+
prebidAdUnit.fetchDemand(adRequestBuilder, resultCode -> {
90+
appendStatusText("Prebid ads loading status: " + resultCode.toString());
91+
loadGamAd(adRequestBuilder);
92+
});
93+
}
94+
95+
private void applyOptableToPrebid(HashMap<String, List<String>> optableTargeting) {
96+
if (optableTargeting != null) {
97+
List<ExternalUserId.UniqueId> uniqueIds = new ArrayList<>();
98+
for (Map.Entry<String, List<String>> entry : optableTargeting.entrySet()) {
99+
List<String> value = entry.getValue();
100+
if (value == null) continue;
101+
for (String id : value) {
102+
uniqueIds.add(new ExternalUserId.UniqueId(id, 1));
103+
}
104+
}
105+
List<ExternalUserId> externalUserIds = new ArrayList<>();
106+
externalUserIds.add(new ExternalUserId("optable.com", uniqueIds));
107+
TargetingParams.setExternalUserIds(externalUserIds);
108+
}
109+
}
110+
111+
private void loadGamAd(AdManagerAdRequest.Builder adRequestBuilder) {
112+
adContainer.removeAllViews();
113+
114+
AdManagerAdRequest adRequest = adRequestBuilder.build();
115+
116+
adView = new AdManagerAdView(requireContext());
117+
adView.setAdUnitId(GAM_AD_UNIT_ID);
118+
adView.setAdSizes(new com.google.android.gms.ads.AdSize(WIDTH, HEIGHT));
119+
adView.setAdListener(new AdListener() {
120+
@Override
121+
public void onAdLoaded() {
122+
super.onAdLoaded();
123+
appendStatusText("Google ad loaded");
124+
}
125+
126+
@Override
127+
public void onAdFailedToLoad(@NonNull @NotNull LoadAdError loadAdError) {
128+
appendStatusText("Google ad failed to load: " + loadAdError.getMessage());
129+
}
130+
});
131+
adView.loadAd(adRequest);
132+
133+
adContainer.addView(adView);
134+
}
135+
136+
/**
137+
* Loads targeting data from cache and then the GAM banner
138+
*/
139+
private void onClickCachedBanner() {
140+
statusTextView.setText("");
141+
142+
AdManagerAdRequest.Builder adRequestBuilder = new AdManagerAdRequest.Builder();
143+
HashMap<String, List<String>> data = MainActivity.OPTABLE.targetingFromCache();
144+
145+
if (data != null) {
146+
changeStatusText("Loaded Optable cached targeting data", data);
147+
for (String key : data.keySet()) {
148+
List<String> values = data.get(key);
149+
if (values == null) continue;
150+
adRequestBuilder.addCustomTargeting(key, values);
151+
}
152+
} else {
153+
changeStatusText("Targeting data cache empty.", null);
154+
}
155+
156+
loadPrebidAd(adRequestBuilder, data);
157+
profile();
158+
witness();
159+
}
160+
161+
/**
162+
* Clears targeting data cache.
163+
*/
164+
private void onClickClearCache() {
165+
statusTextView.setText("Clearing targeting data cache.\n\n");
166+
MainActivity.OPTABLE.targetingClearCache();
167+
}
168+
169+
private void profile() {
170+
HashMap<String, Object> traits = new HashMap<>();
171+
traits.put("gender", "F");
172+
traits.put("age", 38);
173+
traits.put("hasAccount", true);
174+
175+
MainActivity.OPTABLE
176+
.profile(traits)
177+
.observe(getViewLifecycleOwner(), result -> {
178+
if (result.getStatus() == OptableSDK.Status.SUCCESS) {
179+
appendStatusText("Success calling profile API to set traits on user.");
180+
} else {
181+
appendStatusText("Error during sending profile: " + result.getMessage());
182+
}
183+
});
184+
}
185+
186+
private void witness() {
187+
HashMap<String, Object> eventProperties = new HashMap<>();
188+
eventProperties.put("exampleKey", "exampleValue");
189+
eventProperties.put("exampleKey2", 123);
190+
eventProperties.put("exampleKey3", false);
191+
192+
MainActivity.OPTABLE
193+
.witness("GAMBannerFragment.loadAdButtonClicked", eventProperties)
194+
.observe(getViewLifecycleOwner(), result -> {
195+
if (result.getStatus() == OptableSDK.Status.SUCCESS) {
196+
appendStatusText("Success calling witness API to log loadAdButtonClicked event.");
197+
} else {
198+
appendStatusText("Error during sending witness: " + result.getMessage());
199+
}
200+
});
201+
}
202+
203+
private void changeStatusText(@NonNull String message, @Nullable HashMap<String, List<String>> optableResponse) {
204+
StringBuilder formattedMessage = new StringBuilder(message);
205+
if (optableResponse != null) {
206+
formattedMessage.append("\n\nTargeting data: ");
207+
for (Map.Entry<String, ? extends Collection<?>> entry : optableResponse.entrySet()) {
208+
formattedMessage.append(entry.getKey())
209+
.append(" = ")
210+
.append(entry.getValue())
211+
.append("\n");
212+
}
213+
}
214+
statusTextView.setText(formattedMessage.toString());
215+
}
216+
217+
private void appendStatusText(@NonNull String message) {
218+
statusTextView.append("\n\n" + message);
219+
}
220+
221+
}
Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto"
44
android:id="@+id/container"
55
android:layout_width="match_parent"
6-
android:layout_height="match_parent"
7-
android:paddingTop="?attr/actionBarSize">
8-
9-
<com.google.android.material.bottomnavigation.BottomNavigationView
10-
android:id="@+id/nav_view"
11-
android:layout_width="0dp"
12-
android:layout_height="wrap_content"
13-
android:layout_marginStart="0dp"
14-
android:layout_marginEnd="0dp"
15-
android:background="?android:attr/windowBackground"
16-
app:layout_constraintBottom_toBottomOf="parent"
17-
app:layout_constraintLeft_toLeftOf="parent"
18-
app:layout_constraintRight_toRightOf="parent"
19-
app:menu="@menu/bottom_nav_menu" />
6+
android:orientation="vertical"
7+
android:layout_height="match_parent">
208

219
<fragment
2210
android:id="@+id/nav_host_fragment"
2311
android:name="androidx.navigation.fragment.NavHostFragment"
2412
android:layout_width="match_parent"
25-
android:layout_height="match_parent"
13+
android:layout_height="0dp"
14+
android:layout_weight="1"
2615
app:defaultNavHost="true"
27-
app:layout_constraintBottom_toTopOf="@id/nav_view"
28-
app:layout_constraintLeft_toLeftOf="parent"
29-
app:layout_constraintRight_toRightOf="parent"
30-
app:layout_constraintTop_toTopOf="parent"
3116
app:navGraph="@navigation/mobile_navigation" />
3217

33-
</androidx.constraintlayout.widget.ConstraintLayout>
18+
<com.google.android.material.bottomnavigation.BottomNavigationView
19+
android:id="@+id/nav_view"
20+
android:layout_width="match_parent"
21+
android:layout_height="wrap_content"
22+
android:background="?android:attr/windowBackground"
23+
app:menu="@menu/bottom_nav_menu" />
24+
25+
</LinearLayout>

DemoApp/DemoAppJava/app/src/main/res/layout/fragment_gambanner.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
app:layout_constraintTop_toTopOf="parent" />
5858

5959
<TextView
60-
android:id="@+id/targetingDataView"
60+
android:id="@+id/statusTextView"
6161
android:layout_width="291dp"
6262
android:layout_height="346dp"
6363
android:layout_marginTop="100dp"

DemoApp/DemoAppJava/app/src/main/res/layout/fragment_identify.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
xmlns:tools="http://schemas.android.com/tools"
55
android:layout_width="match_parent"
66
android:layout_height="match_parent"
7+
android:paddingTop="50dp"
78
tools:context=".ui.Identify.IdentifyFragment">
89

910
<EditText

0 commit comments

Comments
 (0)