diff --git a/DemoApp/DemoAppJava/app/build.gradle b/DemoApp/DemoAppJava/app/build.gradle index 44e2c9b..cc92e38 100644 --- a/DemoApp/DemoAppJava/app/build.gradle +++ b/DemoApp/DemoAppJava/app/build.gradle @@ -46,6 +46,9 @@ dependencies { // Google Mobile Ads implementation 'com.google.android.gms:play-services-ads:24.6.0' + // Prebid Ads + implementation "org.prebid:prebid-mobile-sdk:3.0.2" + // Base Android implementation "org.jetbrains.kotlin:kotlin-stdlib:2.0.21" implementation 'com.android.support:multidex:1.0.3' diff --git a/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/MainActivity.java b/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/MainActivity.java index ef7ce31..15c8c43 100644 --- a/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/MainActivity.java +++ b/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/MainActivity.java @@ -1,16 +1,22 @@ package co.optable.demoappjava; import android.os.Bundle; +import android.util.Log; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import co.optable.android_sdk.OptableSDK; +import com.google.android.gms.ads.MobileAds; import com.google.android.material.bottomnavigation.BottomNavigationView; +import org.prebid.mobile.PrebidMobile; +import org.prebid.mobile.api.data.InitializationStatus; public class MainActivity extends AppCompatActivity { + private static final String TAG = "MainActivity"; + public static OptableSDK OPTABLE; @Override @@ -20,13 +26,35 @@ protected void onCreate(Bundle savedInstanceState) { MainActivity.OPTABLE = new OptableSDK(this.getApplicationContext(), "sandbox.optable.co", "ios-sdk-demo"); + initGoogleAds(); + initPrebidSdk(); initUi(); } + private void initGoogleAds() { + MobileAds.initialize(this, initializationStatus -> { + }); + } + + private void initPrebidSdk() { + PrebidMobile.setPrebidServerAccountId("0689a263-318d-448b-a3d4-b02e8a709d9d"); + PrebidMobile.initializeSdk(getApplicationContext(), "https://prebid-server-test-j.prebid.org/openrtb2/auction", status -> { + if (status == InitializationStatus.SUCCEEDED) { + Log.d(TAG, "SDK initialized successfully!"); + } else { + Log.e(TAG, "SDK initialization error: " + status.getDescription()); + } + }); + } + private void initUi() { BottomNavigationView navView = findViewById(R.id.nav_view); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); - AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.navigation_identify, R.id.navigation_gambanner).build(); + AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( + R.id.navigation_identify, + R.id.navigation_gambanner, + R.id.navigation_prebid + ).build(); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupWithNavController(navView, navController); } diff --git a/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/GAMBanner/GAMBannerFragment.java b/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/GAMBanner/GAMBannerFragment.java index d717c4f..10112e0 100644 --- a/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/GAMBanner/GAMBannerFragment.java +++ b/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/GAMBanner/GAMBannerFragment.java @@ -34,7 +34,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, private void initUi(View root) { mAdView = root.findViewById(R.id.publisherAdView); - statusTextView = root.findViewById(R.id.targetingDataView); + statusTextView = root.findViewById(R.id.statusTextView); root.findViewById(R.id.btnLoadBanner).setOnClickListener(view -> onClickLoadAd()); root.findViewById(R.id.btnCachedBanner).setOnClickListener(view -> onClickCachedBanner()); diff --git a/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/PrebidBannerFragment.java b/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/PrebidBannerFragment.java new file mode 100644 index 0000000..a6bdd28 --- /dev/null +++ b/DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/ui/PrebidBannerFragment.java @@ -0,0 +1,221 @@ +package co.optable.demoappjava.ui; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import co.optable.android_sdk.OptableSDK; +import co.optable.demoappjava.MainActivity; +import co.optable.demoappjava.R; +import com.google.android.gms.ads.AdListener; +import com.google.android.gms.ads.LoadAdError; +import com.google.android.gms.ads.admanager.AdManagerAdRequest; +import com.google.android.gms.ads.admanager.AdManagerAdView; +import org.jetbrains.annotations.NotNull; +import org.prebid.mobile.BannerAdUnit; +import org.prebid.mobile.ExternalUserId; +import org.prebid.mobile.TargetingParams; + +import java.util.*; + +public class PrebidBannerFragment extends Fragment { + + private static final String GAM_AD_UNIT_ID = "/21808260008/prebid_demo_app_original_api_banner"; + private static final String PREBID_CONFIG_ID = "prebid-demo-banner-320-50"; + private static final int WIDTH = 320; + private static final int HEIGHT = 50; + + private AdManagerAdView adView; + private BannerAdUnit prebidAdUnit; + + private ViewGroup adContainer; + private TextView statusTextView; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.fragment_prebid, container, false); + initUi(root); + return root; + } + + private void initUi(View root) { + statusTextView = root.findViewById(R.id.statusTextView); + adContainer = root.findViewById(R.id.adContainer); + + root.findViewById(R.id.btnLoadBanner).setOnClickListener(view -> onClickLoadAd()); + root.findViewById(R.id.btnCachedBanner).setOnClickListener(view -> onClickCachedBanner()); + root.findViewById(R.id.btnClearCache).setOnClickListener(view -> onClickClearCache()); + } + + /** + * Loads targeting data and then the GAM banner + */ + private void onClickLoadAd() { + statusTextView.setText(""); + + MainActivity.OPTABLE + .targeting() + .observe(getViewLifecycleOwner(), result -> { + AdManagerAdRequest.Builder adRequestBuilder = new AdManagerAdRequest.Builder(); + + if (result.getStatus() == OptableSDK.Status.SUCCESS) { + HashMap> data = result.getData(); + changeStatusText("Loaded Optable targeting data", data); + + if (data != null) { + for (String key : data.keySet()) { + List values = data.get(key); + if (values == null) continue; + adRequestBuilder.addCustomTargeting(key, values); + } + } + } else { + changeStatusText("Error loading Optable targeting data: " + result.getMessage(), null); + } + + loadPrebidAd(adRequestBuilder, result.getData()); + profile(); + witness(); + }); + } + + private void loadPrebidAd(AdManagerAdRequest.Builder adRequestBuilder, @Nullable HashMap> optableTargeting) { + prebidAdUnit = new BannerAdUnit(PREBID_CONFIG_ID, WIDTH, HEIGHT); + applyOptableToPrebid(optableTargeting); + prebidAdUnit.fetchDemand(adRequestBuilder, resultCode -> { + appendStatusText("Prebid ads loading status: " + resultCode.toString()); + loadGamAd(adRequestBuilder); + }); + } + + private void applyOptableToPrebid(HashMap> optableTargeting) { + if (optableTargeting != null) { + List uniqueIds = new ArrayList<>(); + for (Map.Entry> entry : optableTargeting.entrySet()) { + List value = entry.getValue(); + if (value == null) continue; + for (String id : value) { + uniqueIds.add(new ExternalUserId.UniqueId(id, 1)); + } + } + List externalUserIds = new ArrayList<>(); + externalUserIds.add(new ExternalUserId("optable.com", uniqueIds)); + TargetingParams.setExternalUserIds(externalUserIds); + } + } + + private void loadGamAd(AdManagerAdRequest.Builder adRequestBuilder) { + adContainer.removeAllViews(); + + AdManagerAdRequest adRequest = adRequestBuilder.build(); + + adView = new AdManagerAdView(requireContext()); + adView.setAdUnitId(GAM_AD_UNIT_ID); + adView.setAdSizes(new com.google.android.gms.ads.AdSize(WIDTH, HEIGHT)); + adView.setAdListener(new AdListener() { + @Override + public void onAdLoaded() { + super.onAdLoaded(); + appendStatusText("Google ad loaded"); + } + + @Override + public void onAdFailedToLoad(@NonNull @NotNull LoadAdError loadAdError) { + appendStatusText("Google ad failed to load: " + loadAdError.getMessage()); + } + }); + adView.loadAd(adRequest); + + adContainer.addView(adView); + } + + /** + * Loads targeting data from cache and then the GAM banner + */ + private void onClickCachedBanner() { + statusTextView.setText(""); + + AdManagerAdRequest.Builder adRequestBuilder = new AdManagerAdRequest.Builder(); + HashMap> data = MainActivity.OPTABLE.targetingFromCache(); + + if (data != null) { + changeStatusText("Loaded Optable cached targeting data", data); + for (String key : data.keySet()) { + List values = data.get(key); + if (values == null) continue; + adRequestBuilder.addCustomTargeting(key, values); + } + } else { + changeStatusText("Targeting data cache empty.", null); + } + + loadPrebidAd(adRequestBuilder, data); + profile(); + witness(); + } + + /** + * Clears targeting data cache. + */ + private void onClickClearCache() { + statusTextView.setText("Clearing targeting data cache.\n\n"); + MainActivity.OPTABLE.targetingClearCache(); + } + + private void profile() { + HashMap traits = new HashMap<>(); + traits.put("gender", "F"); + traits.put("age", 38); + traits.put("hasAccount", true); + + MainActivity.OPTABLE + .profile(traits) + .observe(getViewLifecycleOwner(), result -> { + if (result.getStatus() == OptableSDK.Status.SUCCESS) { + appendStatusText("Success calling profile API to set traits on user."); + } else { + appendStatusText("Error during sending profile: " + result.getMessage()); + } + }); + } + + private void witness() { + HashMap eventProperties = new HashMap<>(); + eventProperties.put("exampleKey", "exampleValue"); + eventProperties.put("exampleKey2", 123); + eventProperties.put("exampleKey3", false); + + MainActivity.OPTABLE + .witness("GAMBannerFragment.loadAdButtonClicked", eventProperties) + .observe(getViewLifecycleOwner(), result -> { + if (result.getStatus() == OptableSDK.Status.SUCCESS) { + appendStatusText("Success calling witness API to log loadAdButtonClicked event."); + } else { + appendStatusText("Error during sending witness: " + result.getMessage()); + } + }); + } + + private void changeStatusText(@NonNull String message, @Nullable HashMap> optableResponse) { + StringBuilder formattedMessage = new StringBuilder(message); + if (optableResponse != null) { + formattedMessage.append("\n\nTargeting data: "); + for (Map.Entry> entry : optableResponse.entrySet()) { + formattedMessage.append(entry.getKey()) + .append(" = ") + .append(entry.getValue()) + .append("\n"); + } + } + statusTextView.setText(formattedMessage.toString()); + } + + private void appendStatusText(@NonNull String message) { + statusTextView.append("\n\n" + message); + } + +} \ No newline at end of file diff --git a/DemoApp/DemoAppJava/app/src/main/res/layout/activity_main.xml b/DemoApp/DemoAppJava/app/src/main/res/layout/activity_main.xml index 38c0aa8..472e2b8 100644 --- a/DemoApp/DemoAppJava/app/src/main/res/layout/activity_main.xml +++ b/DemoApp/DemoAppJava/app/src/main/res/layout/activity_main.xml @@ -1,33 +1,25 @@ - - - + android:orientation="vertical" + android:layout_height="match_parent"> - \ No newline at end of file + + + \ No newline at end of file diff --git a/DemoApp/DemoAppJava/app/src/main/res/layout/fragment_gambanner.xml b/DemoApp/DemoAppJava/app/src/main/res/layout/fragment_gambanner.xml index 2fe5cbc..1bfeb90 100644 --- a/DemoApp/DemoAppJava/app/src/main/res/layout/fragment_gambanner.xml +++ b/DemoApp/DemoAppJava/app/src/main/res/layout/fragment_gambanner.xml @@ -57,7 +57,7 @@ app:layout_constraintTop_toTopOf="parent" /> + + + + +