From 2606de40408e30014e9614e92508769f95d57a28 Mon Sep 17 00:00:00 2001 From: Steven Bankhead Date: Thu, 18 Mar 2021 18:32:10 -0700 Subject: [PATCH] Release 3.7.0 --- app/build.gradle | 12 +- .../unity3d/ads/example/UnityAdsTestApp.java | 12 +- .../example/ui/main/SectionsPagerAdapter.java | 11 +- .../ads/example/ui/main/UnityAdsFragment.java | 8 +- .../layout/activity_unity_ads_test_app.xml | 12 +- build.gradle | 4 +- gradle.properties | 3 +- unity-ads/build.gradle | 49 +- .../ads/test/InstrumentationTestSuite.java | 24 +- .../com/unity3d/ads/test/TestUtilities.java | 5 + .../ads/test/environment/EnvironmentTest.java | 27 +- .../unity3d/ads/test/hybrid/HybridTest.java | 6 +- .../services/ads/load/LoadModuleTest.java | 429 ------------------ .../ads/operation/AdOperationTests.java | 61 +++ ...uleDecoratorInitializationBufferTests.java | 111 +++++ .../operation/LoadModuleDecoratorTests.java | 49 ++ .../LoadModuleDecoratorTimeoutTests.java | 99 ++++ .../ads/operation/LoadModuleTests.java | 170 +++++++ .../ads/operation/LoadOperationTests.java | 133 ++++++ .../ads/operation/OperationTestUtilities.java | 51 +++ .../ads/operation/ShowModuleTests.java | 207 +++++++++ .../ads/properties/AdsPropertiesTests.java | 2 +- .../ads/webplayer/WebPlayerViewCacheTest.java | 6 +- .../banners/BannerViewCacheTests.java | 4 +- .../InitializationNotificationCenterTest.java | 4 +- .../WebViewBridgeSharedObjectTests.java | 83 ++++ .../WebViewBridgeInvocationTests.java | 133 ++++++ .../banner/BannerIntegrationTest.java | 4 +- .../ads/test/legacy/AdUnitActivityTest.java | 5 +- .../legacy/AdUnitActivityTestBaseClass.java | 23 +- .../legacy/AdvertisingIdentifierTest.java | 6 +- .../ads/test/legacy/BroadcastTest.java | 6 +- .../unity3d/ads/test/legacy/CacheTest.java | 6 +- .../ads/test/legacy/ClientPropertiesTest.java | 10 +- .../ads/test/legacy/ConfigurationTest.java | 15 +- .../ads/test/legacy/ConnectivityTest.java | 36 +- .../unity3d/ads/test/legacy/DeviceTest.java | 7 +- .../ads/test/legacy/EnvironmentCheckTest.java | 2 +- .../unity3d/ads/test/legacy/EventIdTest.java | 6 +- .../ads/test/legacy/InitializeThreadTest.java | 18 +- .../ads/test/legacy/InvocationTest.java | 6 +- .../test/legacy/LifecycleListenerTest.java | 4 +- .../ads/test/legacy/LoadOptionsTest.java | 2 +- .../unity3d/ads/test/legacy/MetaDataTest.java | 6 +- .../ads/test/legacy/NativeCallbackTest.java | 6 +- .../legacy/OpenAdvertisingIdentifierTest.java | 6 +- .../ads/test/legacy/PlacementTest.java | 2 +- .../ads/test/legacy/PreferencesTest.java | 10 +- .../ads/test/legacy/PublicApiTest.java | 2 +- .../unity3d/ads/test/legacy/RequestTest.java | 8 +- .../ads/test/legacy/SdkPropertiesTest.java | 7 +- .../ads/test/legacy/SensorInfoTest.java | 8 +- .../ads/test/legacy/StorageDiskTest.java | 8 +- .../ads/test/legacy/StorageGeneralTest.java | 10 +- .../ads/test/legacy/StorageMemoryTest.java | 8 +- .../ads/test/legacy/TokenStorageTest.java | 4 +- .../ads/test/legacy/UtilitiesTest.java | 4 +- .../ads/test/legacy/VideoViewTest.java | 42 +- .../ads/test/legacy/VolumeChangeTest.java | 14 +- .../ads/test/legacy/WebPlayerViewTest.java | 11 +- .../test/legacy/WebRequestThreadPoolTest.java | 2 +- .../ads/test/legacy/WebViewAppTest.java | 36 +- .../legacy/WebViewBridgeInterfaceTest.java | 6 +- .../ads/test/legacy/WebViewBridgeTest.java | 2 +- .../ads/test/legacy/WebViewCallbackTest.java | 6 +- .../core/api/PlacementContentsTest.java | 2 +- .../core/api/CustomPurchasingTest.java | 3 +- .../unity3d/ads/IUnityAdsLoadListener.java | 4 +- .../unity3d/ads/IUnityAdsShowListener.java | 41 ++ .../main/java/com/unity3d/ads/UnityAds.java | 113 ++++- .../services/ads/UnityAdsImplementation.java | 124 +++-- .../com/unity3d/services/ads/api/Load.java | 9 +- .../com/unity3d/services/ads/api/Show.java | 33 ++ .../configuration/AdsModuleConfiguration.java | 9 +- .../unity3d/services/ads/load/LoadModule.java | 265 ----------- .../services/ads/operation/AdModule.java | 19 + .../services/ads/operation/AdOperation.java | 29 ++ .../services/ads/operation/IAdModule.java | 11 + .../services/ads/operation/IAdOperation.java | 7 + .../ads/operation/load/ILoadModule.java | 9 + .../ads/operation/load/ILoadOperation.java | 8 + .../ads/operation/load/LoadModule.java | 118 +++++ .../operation/load/LoadModuleDecorator.java | 48 ++ ...adModuleDecoratorInitializationBuffer.java | 68 +++ .../load/LoadModuleDecoratorTimeout.java | 66 +++ .../ads/operation/load/LoadOperation.java | 45 ++ .../operation/load/LoadOperationState.java | 33 ++ .../ads/operation/show/IShowModule.java | 11 + .../ads/operation/show/IShowOperation.java | 8 + .../ads/operation/show/ShowModule.java | 142 ++++++ .../operation/show/ShowModuleDecorator.java | 58 +++ .../show/ShowModuleDecoratorTimeout.java | 66 +++ .../ads/operation/show/ShowOperation.java | 65 +++ .../operation/show/ShowOperationState.java | 36 ++ .../core/configuration/Configuration.java | 10 +- .../core/request/ISDKMetricSender.java | 8 + .../core/request/SDKMetricEvents.java | 10 + .../core/request/SDKMetricSender.java | 19 + .../services/core/webview/WebViewApp.java | 5 +- .../webview/bridge/IWebViewBridgeInvoker.java | 7 + .../IWebViewBridgeSharedObjectStore.java | 7 + .../webview/bridge/IWebViewSharedObject.java | 5 + .../webview/bridge/WebViewBridgeInvoker.java | 12 + .../WebViewBridgeSharedObjectStore.java | 26 ++ .../invocation/IWebViewBridgeInvocation.java | 5 + .../IWebViewBridgeInvocationCallback.java | 9 + .../invocation/WebViewBridgeInvocation.java | 76 ++++ 107 files changed, 2679 insertions(+), 1049 deletions(-) delete mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/load/LoadModuleTest.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/AdOperationTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorInitializationBufferTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTimeoutTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadOperationTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/OperationTestUtilities.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/WebViewBridgeSharedObjectTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationTests.java create mode 100644 unity-ads/src/main/java/com/unity3d/ads/IUnityAdsShowListener.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/api/Show.java delete mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/load/LoadModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/AdModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/AdOperation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdOperation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadOperation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecorator.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorInitializationBuffer.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorTimeout.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperationState.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowOperation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModule.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecorator.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecoratorTimeout.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperationState.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/request/ISDKMetricSender.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricEvents.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricSender.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeInvoker.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeSharedObjectStore.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewSharedObject.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeInvoker.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeSharedObjectStore.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocation.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocationCallback.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocation.java diff --git a/app/build.gradle b/app/build.gradle index c2fa4273..6766dcda 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.unity3d.ads.example" minSdkVersion 19 targetSdkVersion 30 - versionCode = 3620 - versionName = "3.6.2" + versionCode = 3700 + versionName = "3.7.0" } flavorDimensions "arEnabled" @@ -36,10 +36,10 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:26.1.0' - implementation 'com.android.support:design:26.1.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'android.arch.lifecycle:extensions:1.1.1' + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' arImplementation 'com.google.ar:core:1.4.0' implementation project(':unity-ads') } diff --git a/app/src/main/java/com/unity3d/ads/example/UnityAdsTestApp.java b/app/src/main/java/com/unity3d/ads/example/UnityAdsTestApp.java index cebffa4e..820c38a8 100644 --- a/app/src/main/java/com/unity3d/ads/example/UnityAdsTestApp.java +++ b/app/src/main/java/com/unity3d/ads/example/UnityAdsTestApp.java @@ -1,15 +1,9 @@ package com.unity3d.ads.example; import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; -import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; -import android.support.v7.app.AppCompatActivity; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.AppCompatActivity; +import com.google.android.material.tabs.TabLayout; import com.unity3d.ads.example.ui.main.SectionsPagerAdapter; public class UnityAdsTestApp extends AppCompatActivity { diff --git a/app/src/main/java/com/unity3d/ads/example/ui/main/SectionsPagerAdapter.java b/app/src/main/java/com/unity3d/ads/example/ui/main/SectionsPagerAdapter.java index 4d5dde94..2073abae 100644 --- a/app/src/main/java/com/unity3d/ads/example/ui/main/SectionsPagerAdapter.java +++ b/app/src/main/java/com/unity3d/ads/example/ui/main/SectionsPagerAdapter.java @@ -1,13 +1,10 @@ package com.unity3d.ads.example.ui.main; import android.content.Context; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; - -import com.unity3d.ads.example.R; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; /** * A [FragmentPagerAdapter] that returns a fragment corresponding to diff --git a/app/src/main/java/com/unity3d/ads/example/ui/main/UnityAdsFragment.java b/app/src/main/java/com/unity3d/ads/example/ui/main/UnityAdsFragment.java index b70ded86..bb97619b 100644 --- a/app/src/main/java/com/unity3d/ads/example/ui/main/UnityAdsFragment.java +++ b/app/src/main/java/com/unity3d/ads/example/ui/main/UnityAdsFragment.java @@ -8,8 +8,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.animation.AlphaAnimation; import android.widget.Button; import android.widget.CheckBox; @@ -29,12 +29,8 @@ import com.unity3d.services.banners.view.BannerPosition; import com.unity3d.services.core.log.DeviceLog; import com.unity3d.services.core.misc.Utilities; -import com.unity3d.services.core.properties.SdkProperties; import com.unity3d.services.core.webview.WebView; -import java.net.MalformedURLException; -import java.net.URISyntaxException; - /** * A placeholder fragment containing a simple view. */ diff --git a/app/src/main/res/layout/activity_unity_ads_test_app.xml b/app/src/main/res/layout/activity_unity_ads_test_app.xml index c103c6b8..3f1f8f3b 100644 --- a/app/src/main/res/layout/activity_unity_ads_test_app.xml +++ b/app/src/main/res/layout/activity_unity_ads_test_app.xml @@ -1,12 +1,12 @@ - - @@ -21,17 +21,17 @@ android:text="@string/app_name" android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" /> - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index ed30e2dd..533071f4 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,9 @@ buildscript { jcenter() google() } - dependencies { - classpath 'com.android.tools.build:gradle:3.1.4' + dependencies { + classpath 'com.android.tools.build:gradle:3.2.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' diff --git a/gradle.properties b/gradle.properties index 1d3591c8..ccd5dda1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,5 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +android.useAndroidX=true \ No newline at end of file diff --git a/unity-ads/build.gradle b/unity-ads/build.gradle index 706420bf..0efd8431 100644 --- a/unity-ads/build.gradle +++ b/unity-ads/build.gradle @@ -17,7 +17,7 @@ ext { siteUrl = 'https://github.com/Unity-Technologies/unity-ads-android' gitUrl = 'https://github.com/Unity-Technologies/unity-ads-android.git' - libraryVersion = '3.6.2' + libraryVersion = '3.7.0' developerId = 'sbankhead' developerName = 'Steven Bankhead' @@ -31,7 +31,7 @@ ext { version = libraryVersion android { - compileSdkVersion 26 + compileSdkVersion 30 buildToolsVersion '28.0.2' com.android.ddmlib.DdmPreferences.setLogLevel("verbose") com.android.ddmlib.DdmPreferences.setTimeOut(10 * 60000) @@ -40,7 +40,7 @@ android { defaultConfig { minSdkVersion 19 - targetSdkVersion 29 + targetSdkVersion 30 /* Please ensure that the last two digits of version number does not exceed 50 unless it is a China SDK. This is because adding 50 to the version number is a one-to-one @@ -51,16 +51,15 @@ android { All SDK with version numbers with last two digits >= 50 will be treated as China SDK for filtering in the backend. */ - versionCode = 3620 - versionName = "3.6.2" + versionCode = 3700 + versionName = "3.7.0" setProperty("archivesBaseName", "unity-ads") - buildConfigField 'String', 'WEBVIEW_BRANCH', getPropertyStringWithDefaultValue('WEBVIEW_BRANCH', '"' + versionName + '"') - + buildConfigField('String', 'WEBVIEW_BRANCH', getPropertyStringWithDefaultValue('WEBVIEW_BRANCH', '"' + versionName + '"')) testBuildType "debug" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunnerArguments disableAnalytics: 'true' // Won't work yet, see: https://code.google.com/p/android/issues/detail?id=188241 } @@ -99,31 +98,31 @@ android { dependencies { testImplementation 'junit:junit:4.12' - androidTestImplementation 'org.mockito:mockito-core:2.19.1' - androidTestImplementation 'org.mockito:mockito-android:2.19.1' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test:rules:1.0.2' + androidTestImplementation 'org.mockito:mockito-core:2.25.0' + androidTestImplementation 'org.mockito:mockito-android:2.25.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test:rules:1.1.1' compileOnly 'com.google.ar:core:1.0.0' } task javadoc(type: Javadoc, overwrite: true) { - description "Generates Javadoc for Release" - source = android.sourceSets.main.java.srcDirs - ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" - doFirst { - classpath = project.files(android.getBootClasspath().join(File.pathSeparator)) + files(ext.androidJar) - } + description "Generates Javadoc for Release" + source = android.sourceSets.main.java.srcDirs + ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" + doFirst { + classpath = project.files(android.getBootClasspath().join(File.pathSeparator)) + files(ext.androidJar) + } options { links "http://docs.oracle.com/javase/7/docs/api/" linksOffline "http://developer.android.com/reference","${android.sdkDirectory}/docs/reference" - } - exclude '**/R.java' - exclude 'com/unity3d/services/ar/view/GLSurfaceView.java' - exclude 'com/unity3d/services/ar/api/AR.java' - exclude 'com/unity3d/services/ar/view/ARView.java' - exclude 'com/unity3d/services/ar/view/ARViewHandler.java' - destinationDir = file("../javadoc/") } + exclude '**/R.java' + exclude 'com/unity3d/services/ar/view/GLSurfaceView.java' + exclude 'com/unity3d/services/ar/api/AR.java' + exclude 'com/unity3d/services/ar/view/ARView.java' + exclude 'com/unity3d/services/ar/view/ARViewHandler.java' + destinationDir = file("../javadoc/") +} task androidJavadocsJar(type: Jar, dependsOn: javadoc) { classifier = "javadoc" diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/InstrumentationTestSuite.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/InstrumentationTestSuite.java index a0f08d52..7100d461 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/InstrumentationTestSuite.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/InstrumentationTestSuite.java @@ -1,11 +1,20 @@ package com.unity3d.ads.test; -import com.unity3d.ads.test.instrumentation.services.ads.load.LoadModuleTest; +import com.unity3d.ads.test.instrumentation.services.ads.operation.AdOperationTests; +import com.unity3d.ads.test.instrumentation.services.ads.operation.LoadModuleDecoratorInitializationBufferTests; +import com.unity3d.ads.test.instrumentation.services.ads.operation.LoadModuleDecoratorTests; +import com.unity3d.ads.test.instrumentation.services.ads.operation.LoadModuleDecoratorTimeoutTests; +import com.unity3d.ads.test.instrumentation.services.ads.operation.LoadModuleTests; +import com.unity3d.ads.test.instrumentation.services.ads.operation.LoadOperationTests; +import com.unity3d.ads.test.instrumentation.services.ads.operation.ShowModuleTests; import com.unity3d.ads.test.instrumentation.services.ads.properties.AdsPropertiesTests; import com.unity3d.ads.test.instrumentation.services.ads.webplayer.WebPlayerViewCacheTest; import com.unity3d.ads.test.instrumentation.services.ads.webplayer.WebPlayerViewSettingsCacheTest; import com.unity3d.ads.test.instrumentation.services.banners.BannerViewCacheTests; import com.unity3d.ads.test.instrumentation.services.core.configuration.InitializationNotificationCenterTest; +import com.unity3d.ads.test.instrumentation.services.core.webview.bridge.WebViewBridgeSharedObjectTests; +import com.unity3d.ads.test.instrumentation.services.core.webview.bridge.invocation.WebViewBridgeInvocationTests; +import com.unity3d.ads.test.legacy.ConfigurationTest; import com.unity3d.services.analytics.AcquisitionTypeTest; import com.unity3d.services.analytics.UnityAnalyticsTest; @@ -18,11 +27,20 @@ AcquisitionTypeTest.class, AdsPropertiesTests.class, InitializationNotificationCenterTest.class, - LoadModuleTest.class, // LoadBridgeTest.class, WebPlayerViewSettingsCacheTest.class, WebPlayerViewCacheTest.class, - BannerViewCacheTests.class + BannerViewCacheTests.class, + WebViewBridgeSharedObjectTests.class, + WebViewBridgeInvocationTests.class, + LoadOperationTests.class, + LoadModuleTests.class, + LoadModuleDecoratorTimeoutTests.class, + LoadModuleDecoratorTests.class, + LoadModuleDecoratorInitializationBufferTests.class, + AdOperationTests.class, + ShowModuleTests.class, + ConfigurationTest.class }) public class InstrumentationTestSuite {} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/TestUtilities.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/TestUtilities.java index 1ace5e47..c9ced189 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/TestUtilities.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/TestUtilities.java @@ -60,4 +60,9 @@ public static T[] concat(T[] first, T[] second) { return result; } + public static void SleepCurrentThread(int timeInMilliseconds) { + try { + Thread.sleep(timeInMilliseconds); + } catch (InterruptedException e) { } + } } diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/environment/EnvironmentTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/environment/EnvironmentTest.java index 16cc8018..40864004 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/environment/EnvironmentTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/environment/EnvironmentTest.java @@ -4,41 +4,40 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + import android.telephony.TelephonyManager; -import android.test.ActivityInstrumentationTestCase2; import com.unity3d.services.ads.adunit.AdUnitActivity; import com.unity3d.services.core.request.WebRequest; import com.unity3d.ads.test.TestUtilities; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + @RunWith(AndroidJUnit4.class) -public class EnvironmentTest extends ActivityInstrumentationTestCase2 { +public class EnvironmentTest extends ActivityTestRule { public EnvironmentTest () { super(AdUnitActivity.class); } - @Before - public void beforeTest () { - injectInstrumentation(InstrumentationRegistry.getInstrumentation()); - } - @Test public void testWifiEnabled () { ConnectivityManager mConnectivity; boolean isWifi = false; - mConnectivity = (ConnectivityManager) InstrumentationRegistry.getTargetContext().getSystemService(Context.CONNECTIVITY_SERVICE); + mConnectivity = (ConnectivityManager) InstrumentationRegistry.getInstrumentation().getTargetContext().getSystemService(Context.CONNECTIVITY_SERVICE); TelephonyManager mTelephony; - mTelephony = (TelephonyManager)InstrumentationRegistry.getTargetContext().getSystemService(Context.TELEPHONY_SERVICE); + mTelephony = (TelephonyManager)InstrumentationRegistry.getInstrumentation().getTargetContext().getSystemService(Context.TELEPHONY_SERVICE); // Skip if no connection, or background data disabled if (mConnectivity != null) { @@ -68,7 +67,7 @@ public void testRequest () throws Exception { @Test public void testStorageAccess () { - File cacheDir = getCacheDirectory(InstrumentationRegistry.getTargetContext()); + File cacheDir = getCacheDirectory(InstrumentationRegistry.getInstrumentation().getTargetContext()); assertNotNull("Cache directory is null", cacheDir); assertTrue("Cannot read cache directory: " + cacheDir, cacheDir.canRead()); assertTrue("Cannot write to cache directory: " + cacheDir, cacheDir.canWrite()); @@ -76,7 +75,7 @@ public void testStorageAccess () { @Test public void testStorageSpace () { - File cacheDir = getCacheDirectory(InstrumentationRegistry.getTargetContext()); + File cacheDir = getCacheDirectory(InstrumentationRegistry.getInstrumentation().getTargetContext()); assertNotNull("Cache directory is null", cacheDir); assertTrue("Target cache (" + cacheDir + ") doesn't have enough space left", cacheDir.getFreeSpace() > 2000000); assertTrue("Cannot read cache directory: " + cacheDir, cacheDir.canRead()); diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/hybrid/HybridTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/hybrid/HybridTest.java index 79816b4d..543b238d 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/hybrid/HybridTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/hybrid/HybridTest.java @@ -1,12 +1,11 @@ package com.unity3d.ads.test.hybrid; import android.os.Build; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.ads.UnityAds; import com.unity3d.services.core.configuration.Configuration; -import com.unity3d.services.core.configuration.IModuleConfiguration; import com.unity3d.services.core.configuration.InitializeThread; import com.unity3d.services.core.misc.Utilities; import com.unity3d.services.core.properties.ClientProperties; @@ -23,7 +22,6 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/load/LoadModuleTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/load/LoadModuleTest.java deleted file mode 100644 index 6e541d5b..00000000 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/load/LoadModuleTest.java +++ /dev/null @@ -1,429 +0,0 @@ -package com.unity3d.ads.test.instrumentation.services.ads.load; - -import android.support.test.runner.AndroidJUnit4; - -import com.unity3d.ads.IUnityAdsLoadListener; -import com.unity3d.ads.UnityAdsLoadOptions; -import com.unity3d.services.ads.load.LoadModule; -import com.unity3d.services.core.configuration.IInitializationListener; -import com.unity3d.services.core.configuration.IInitializationNotificationCenter; -import com.unity3d.services.core.configuration.InitializationNotificationCenter; -import com.unity3d.services.core.properties.SdkProperties; -import com.unity3d.services.core.webview.WebViewApp; -import com.unity3d.services.core.webview.bridge.CallbackStatus; - -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -@RunWith(AndroidJUnit4.class) -public class LoadModuleTest { - - private InitializationNotificationCenter initializationNotificationCenter; - private LoadModule loadModule; - private IInitializationNotificationCenter notificationCenter; - - private IInitializationListener listener; - - @Before - public void before() { - notificationCenter = mock(IInitializationNotificationCenter.class); - - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - listener = invocation.getArgument(0); - return null; - }}) - .when(notificationCenter).addListener(any(IInitializationListener.class)); - - loadModule = new LoadModule(notificationCenter); - } - - @Test - public void testSubscription() { - verify(notificationCenter, times(1)).addListener(loadModule); - verify(notificationCenter, times(1)).addListener(any(IInitializationListener.class)); - } - - @Test - public void testLoadAfterInitialized() throws InterruptedException, InvocationTargetException, IllegalAccessException, JSONException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testLoadWithHeaderBiddingOptions() throws InterruptedException, InvocationTargetException, IllegalAccessException, JSONException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - String objectId = "1234"; - String adMarkup = "Test"; - UnityAdsLoadOptions options = new UnityAdsLoadOptions(); - options.setObjectId(objectId); - options.setAdMarkup(adMarkup); - - loadModule.load("test", options, mockedLoadListener.listener); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener.listener); - - JSONObject invocationOptions = mockedWebViewApp.getOptions(); - - assertNotNull("Test load with header bidding options: invocation options are null", invocationOptions); - assertTrue("Test load with header bidding options: header bidding options are missing", invocationOptions.has("options")); - - JSONObject headerBiddingOptions = invocationOptions.getJSONObject("options"); - assertTrue("Test load with header bidding options: object_id is missing", headerBiddingOptions.has("objectId")); - assertTrue("Test load with header bidding options: ad_markup is missing", headerBiddingOptions.has("adMarkup")); - assertEquals("Test load with header bidding options: incorrect object id", objectId, headerBiddingOptions.getString("objectId")); - assertEquals("Test load with header bidding options: incorrect ad markup", adMarkup, headerBiddingOptions.getString("adMarkup")); - } - - @Test - public void testLoadAfterInitialized_WithWebViewTimeout() throws InterruptedException, InvocationTargetException, IllegalAccessException, JSONException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedWebViewApp.await(); - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsFailedToLoad("test"); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testLoadAfterInitialized_ListenerCleanup() throws InterruptedException, InvocationTargetException, IllegalAccessException, JSONException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - mockedWebViewApp.simulateLoadCall("test"); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testLoadAfterInitialized_WithFailedWebViewCall() throws InterruptedException, InvocationTargetException, IllegalAccessException, JSONException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedWebViewApp.await(); - mockedWebViewApp.failLoadCall("test"); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsFailedToLoad("test"); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testLoadAfterInitialized_WithHardcodedTimeout() throws InterruptedException, InvocationTargetException, IllegalAccessException, JSONException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCallTimeout("test"); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsFailedToLoad("test"); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testTwiceLoadAfterInitialized() throws InterruptedException, JSONException, InvocationTargetException, IllegalAccessException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener1 = new MockedLoadListener(); - MockedLoadListener mockedLoadListener2 = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener1.listener); - loadModule.load("test_2", new UnityAdsLoadOptions(), mockedLoadListener2.listener); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test_2"); - - mockedLoadListener1.await(); - mockedLoadListener2.await(); - - verify(mockedLoadListener1.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener1.listener); - - verify(mockedLoadListener2.listener, Mockito.times(1)).onUnityAdsAdLoaded("test_2"); - verifyNoMoreInteractions(mockedLoadListener2.listener); - } - - @Test - public void testLoadWithNullPlacement() throws InterruptedException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load(null, new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsFailedToLoad(null); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testLoadWithEmptyPlacement() throws InterruptedException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - - MockedLoadListener mockedLoadListener = new MockedLoadListener(); - - loadModule.load("", new UnityAdsLoadOptions(), mockedLoadListener.listener); - - mockedLoadListener.await(); - - verify(mockedLoadListener.listener, Mockito.times(1)).onUnityAdsFailedToLoad(""); - verifyNoMoreInteractions(mockedLoadListener.listener); - } - - @Test - public void testLoadBeforeInitializedAndBatchInvocation() throws InterruptedException, IllegalAccessException, JSONException, InvocationTargetException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); - - MockedLoadListener mockedLoadListener1 = new MockedLoadListener(); - MockedLoadListener mockedLoadListener2 = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener1.listener); - loadModule.load("test_2", new UnityAdsLoadOptions(), mockedLoadListener2.listener); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - loadModule.onSdkInitialized(); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test_2"); - - mockedLoadListener1.await(); - mockedLoadListener2.await(); - - verify(mockedLoadListener1.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener1.listener); - - verify(mockedLoadListener2.listener, Mockito.times(1)).onUnityAdsAdLoaded("test_2"); - verifyNoMoreInteractions(mockedLoadListener2.listener); - } - - @Test - public void testLoadBeforeInitializedAndBatchInvocation_InitFailed() throws InterruptedException, IllegalAccessException, JSONException, InvocationTargetException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); - - MockedLoadListener mockedLoadListener1 = new MockedLoadListener(); - MockedLoadListener mockedLoadListener2 = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener1.listener); - loadModule.load("test_2", new UnityAdsLoadOptions(), mockedLoadListener2.listener); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_FAILED); - loadModule.onSdkInitializationFailed("", 0); - - mockedLoadListener1.await(); - mockedLoadListener2.await(); - - verify(mockedLoadListener1.listener, Mockito.times(1)).onUnityAdsFailedToLoad("test"); - verifyNoMoreInteractions(mockedLoadListener1.listener); - - verify(mockedLoadListener2.listener, Mockito.times(1)).onUnityAdsFailedToLoad("test_2"); - verifyNoMoreInteractions(mockedLoadListener2.listener); - } - - @Test - public void testLoadBeforeInitializedSamePlacement() throws InterruptedException, IllegalAccessException, JSONException, InvocationTargetException { - MockedWebViewApp mockedWebViewApp = new MockedWebViewApp(loadModule); - WebViewApp.setCurrentApp(mockedWebViewApp.webViewApp); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); - - MockedLoadListener mockedLoadListener1 = new MockedLoadListener(); - MockedLoadListener mockedLoadListener2 = new MockedLoadListener(); - - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener1.listener); - loadModule.load("test", new UnityAdsLoadOptions(), mockedLoadListener2.listener); - - SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); - loadModule.onSdkInitialized(); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - - mockedWebViewApp.await(); - mockedWebViewApp.simulateLoadCall("test"); - - mockedLoadListener1.await(); - mockedLoadListener2.await(); - - verify(mockedLoadListener1.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener1.listener); - - verify(mockedLoadListener2.listener, Mockito.times(1)).onUnityAdsAdLoaded("test"); - verifyNoMoreInteractions(mockedLoadListener2.listener); - } - - class MockedLoadListener { - public CountDownLatch latch; - public IUnityAdsLoadListener listener; - - public MockedLoadListener() { - latch = new CountDownLatch(1); - listener = mock(IUnityAdsLoadListener.class); - - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - latch.countDown(); - return null; - }}).when(listener).onUnityAdsAdLoaded(anyString()); - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - latch.countDown(); - return null; - }}).when(listener).onUnityAdsFailedToLoad(anyString()); - } - - public void await() throws InterruptedException { - latch.await(35, TimeUnit.SECONDS); - } - } - - class MockedWebViewApp { - private final LoadModule loadModule; - public CountDownLatch latch; - public WebViewApp webViewApp; - - public Method loadCallback; - public JSONObject options; - - public MockedWebViewApp(LoadModule loadModule) { - webViewApp = mock(WebViewApp.class); - latch = new CountDownLatch(1); - this.loadModule = loadModule; - - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - loadCallback = invocation.getArgument(2); - options = invocation.getArgument(3); - latch.countDown(); - return null; - }}).when(webViewApp).invokeMethod(eq("webview"), eq("load"), any(Method.class), any()); - } - - public void await() throws InterruptedException { - latch.await(35, TimeUnit.SECONDS); - latch = new CountDownLatch(1); - } - - public void failLoadCall(String placementId) throws InvocationTargetException, IllegalAccessException, JSONException { - loadCallback.invoke(null, CallbackStatus.ERROR); - } - - public void simulateLoadCall(String placementId) throws InvocationTargetException, IllegalAccessException, JSONException { - loadCallback.invoke(null, CallbackStatus.OK); - loadModule.sendAdLoaded(placementId, options.getString("listenerId")); - } - - public void simulateLoadCallTimeout(String placementId) throws InvocationTargetException, IllegalAccessException, JSONException { - loadCallback.invoke(null, CallbackStatus.OK); - } - - public JSONObject getOptions() { - return options; - } - } - -} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/AdOperationTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/AdOperationTests.java new file mode 100644 index 00000000..28eba508 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/AdOperationTests.java @@ -0,0 +1,61 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.services.ads.operation.AdOperation; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocation; + +import junit.framework.Assert; + +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.UUID; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class AdOperationTests { + //Test Implementation of AdOperation + private class AdOperationTestImplementation extends AdOperation { + protected AdOperationTestImplementation(IWebViewBridgeInvocation webViewBridgeInvocation, String invocationMethodName) { + super(webViewBridgeInvocation, invocationMethodName); + } + + @Override + public String getId() { + return UUID.randomUUID().toString(); + } + } + + private static String invocationMethodName = "testMethodName"; + private static int timeoutLength = 1000; + private static Object[] testData = new Object[]{"RandomTestDataObjectAsString", 12}; + + @Test + public void invokeCallsWebViewBridgeInvocation() { + WebViewBridgeInvocation webViewBridgeInvocationMock = mock(WebViewBridgeInvocation.class); + AdOperationTestImplementation adOperation = new AdOperationTestImplementation(webViewBridgeInvocationMock, invocationMethodName); + + adOperation.invoke(timeoutLength, testData); + + Mockito.verify(webViewBridgeInvocationMock, times(1)).invoke("webview", invocationMethodName, timeoutLength, testData); + } + + @Test(expected = IllegalArgumentException.class) + public void AdOperationCallsIllegalArgumentExceptionWhenInvocationIsNull() { + AdOperationTestImplementation adOperation = new AdOperationTestImplementation(null, invocationMethodName); + adOperation.invoke(timeoutLength, testData); + } + + @Test(expected = IllegalArgumentException.class) + public void AdOperationCallsIllegalArgumentExceptionWhenMethodNameIsNull() { + AdOperationTestImplementation adOperation = new AdOperationTestImplementation(mock(WebViewBridgeInvocation.class), null); + adOperation.invoke(timeoutLength, testData); + } + + @Test + public void getIdIsNotNull() { + AdOperationTestImplementation adOperation = new AdOperationTestImplementation(mock(WebViewBridgeInvocation.class), invocationMethodName); + Assert.assertNotNull(adOperation.getId()); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorInitializationBufferTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorInitializationBufferTests.java new file mode 100644 index 00000000..6d33f959 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorInitializationBufferTests.java @@ -0,0 +1,111 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.ads.IUnityAdsLoadListener; +import com.unity3d.ads.UnityAds; +import com.unity3d.ads.UnityAdsLoadOptions; +import com.unity3d.ads.test.TestUtilities; +import com.unity3d.services.ads.operation.load.ILoadModule; +import com.unity3d.services.ads.operation.load.LoadOperationState; +import com.unity3d.services.ads.operation.load.LoadModuleDecoratorInitializationBuffer; +import com.unity3d.services.core.configuration.Configuration; +import com.unity3d.services.core.configuration.IInitializationNotificationCenter; +import com.unity3d.services.core.properties.SdkProperties; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class LoadModuleDecoratorInitializationBufferTests { + private static String testPlacementId = "TestPlacementId"; + private static int uiThreadDelay = 25; + + private ILoadModule loadModuleMock = mock(ILoadModule.class); + private IInitializationNotificationCenter initializationNotificationCenterMock; + private IWebViewBridgeInvoker webViewBridgeInvokerMock; + private LoadOperationState loadOperationStateMock; + + @Before + public void beforeEachTest() { + loadModuleMock = mock(ILoadModule.class); + initializationNotificationCenterMock = mock(IInitializationNotificationCenter.class); + webViewBridgeInvokerMock = mock(IWebViewBridgeInvoker.class); + loadOperationStateMock = mock(LoadOperationState.class); + } + + @Test + public void executeAdOperationCallsLoadModuleExecuteAdOperationWhenSdkIsInitialized() { + LoadModuleDecoratorInitializationBuffer loadModuleInitBuffer = new LoadModuleDecoratorInitializationBuffer(loadModuleMock, initializationNotificationCenterMock); + SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY); + + loadModuleInitBuffer.executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + + Mockito.verify(loadModuleMock, times(1)).executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + } + + @Test + public void executeAdOperationCallsListenerOnUnityAdsFailedToLoadWhenInitHasFailed() { + IUnityAdsLoadListener loadListenerMock = mock(IUnityAdsLoadListener.class); + LoadOperationState loadOperationState = new LoadOperationState(testPlacementId, loadListenerMock, new UnityAdsLoadOptions(), new Configuration()); + LoadModuleDecoratorInitializationBuffer loadModuleInitBuffer = new LoadModuleDecoratorInitializationBuffer(loadModuleMock, initializationNotificationCenterMock); + SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZED_FAILED); + + loadModuleInitBuffer.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(testPlacementId, UnityAds.UnityAdsLoadError.INITIALIZE_FAILED, "[UnityAds] SDK Initialization Failed"); + } + + @Test + public void executeAdOperationCallsInitNotificationCenterWhenSdkIsInitializing() { + LoadModuleDecoratorInitializationBuffer loadModuleInitBuffer = new LoadModuleDecoratorInitializationBuffer(loadModuleMock, initializationNotificationCenterMock); + SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); + + loadModuleInitBuffer.executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + + Mockito.verify(initializationNotificationCenterMock, times(1)).addListener(loadModuleInitBuffer); + } + + @Test + public void loadModuleExecuteAdOperationIsCalledWhenOnSdkInitializedIsCalledAfterCallingExecuteAdOperation() { + LoadModuleDecoratorInitializationBuffer loadModuleInitBuffer = new LoadModuleDecoratorInitializationBuffer(loadModuleMock, initializationNotificationCenterMock); + SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); + + loadModuleInitBuffer.executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + Mockito.verify(initializationNotificationCenterMock, times(1)).addListener(loadModuleInitBuffer); + + loadModuleInitBuffer.onSdkInitialized(); + Mockito.verify(loadModuleMock, times(1)).executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + } + + @Test + public void loadModuleExecuteAdOperationIsNotCalledWhenOnSdkInitializedIsCalledAfterCallingExecuteAdOperationAndLoadEventStateNull() { + LoadModuleDecoratorInitializationBuffer loadModuleInitBuffer = new LoadModuleDecoratorInitializationBuffer(loadModuleMock, initializationNotificationCenterMock); + SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); + + loadModuleInitBuffer.executeAdOperation(webViewBridgeInvokerMock, null); + Mockito.verify(initializationNotificationCenterMock, times(1)).addListener(loadModuleInitBuffer); + + loadModuleInitBuffer.onSdkInitialized(); + Mockito.verify(loadModuleMock, times(0)).executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + } + + @Test + public void onUnityAdsFailedToLoadIsCalledWhenOnSdkInitializationFailedIsCalledAfterCallingExecuteAdOperation() { + IUnityAdsLoadListener loadListenerMock = mock(IUnityAdsLoadListener.class); + LoadOperationState loadOperationStateMock = new LoadOperationState(testPlacementId, loadListenerMock, new UnityAdsLoadOptions(), new Configuration()); + LoadModuleDecoratorInitializationBuffer loadModuleInitBuffer = new LoadModuleDecoratorInitializationBuffer(loadModuleMock, initializationNotificationCenterMock); + SdkProperties.setInitializeState(SdkProperties.InitializationState.INITIALIZING); + + loadModuleInitBuffer.executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(initializationNotificationCenterMock, times(1)).addListener(loadModuleInitBuffer); + + loadModuleInitBuffer.onSdkInitializationFailed("UntestableMessage", 0); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(testPlacementId, UnityAds.UnityAdsLoadError.INITIALIZE_FAILED, "[UnityAds] SDK Initialization Failure"); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTests.java new file mode 100644 index 00000000..12305be3 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTests.java @@ -0,0 +1,49 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.load.ILoadModule; +import com.unity3d.services.ads.operation.load.LoadOperationState; +import com.unity3d.services.ads.operation.load.LoadModuleDecorator; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class LoadModuleDecoratorTests { + private static String testId = "TestId"; + + private ILoadModule loadModuleMock; + + @Before + public void beforeEachTest() { + loadModuleMock = mock(ILoadModule.class); + } + + @Test + public void executeAdOperationCallsLoadModuleExecuteAdOperation() { + LoadOperationState loadOperationStateMock = mock(LoadOperationState.class); + IWebViewBridgeInvoker webViewBridgeInvokerMock = mock(IWebViewBridgeInvoker.class); + LoadModuleDecorator loadModuleDecorator = new LoadModuleDecorator(loadModuleMock); + + loadModuleDecorator.executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + Mockito.verify(loadModuleMock, times(1)).executeAdOperation(webViewBridgeInvokerMock, loadOperationStateMock); + } + + @Test + public void onUnityAdsLoadedCallsLoadModuleOnUnityAdsLoaded() { + LoadModuleDecorator loadModuleDecorator = new LoadModuleDecorator(loadModuleMock); + loadModuleDecorator.onUnityAdsAdLoaded(testId); + Mockito.verify(loadModuleMock, times(1)).onUnityAdsAdLoaded(testId); + } + + @Test + public void onUnityAdsFailedToLoadCallsLoadModuleOnUnityAdsFailedToLoad() { + LoadModuleDecorator loadModuleDecorator = new LoadModuleDecorator(loadModuleMock); + loadModuleDecorator.onUnityAdsFailedToLoad(testId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, "ErrorMessage"); + Mockito.verify(loadModuleMock, times(1)).onUnityAdsFailedToLoad(testId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, "ErrorMessage"); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTimeoutTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTimeoutTests.java new file mode 100644 index 00000000..ee56a81d --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleDecoratorTimeoutTests.java @@ -0,0 +1,99 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.ads.IUnityAdsLoadListener; +import com.unity3d.ads.UnityAds; +import com.unity3d.ads.UnityAdsLoadOptions; +import com.unity3d.ads.test.TestUtilities; +import com.unity3d.services.ads.operation.load.ILoadModule; +import com.unity3d.services.ads.operation.load.LoadOperationState; +import com.unity3d.services.ads.operation.load.LoadModuleDecoratorTimeout; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.internal.matchers.Any; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class LoadModuleDecoratorTimeoutTests { + private static String placementId = "TestPlacementId"; + private static UnityAds.UnityAdsLoadError loadError = UnityAds.UnityAdsLoadError.INTERNAL_ERROR; + private static String loadErrorMessage = "LoadErrorMessage"; + private static UnityAdsLoadOptions loadOptions = new UnityAdsLoadOptions(); + + private static int loadTimeout = 50; + private static int uiThreadDelay = 150; + + private IUnityAdsLoadListener loadListenerMock; + private ILoadModule loadModuleMock; + + @Before + public void beforeEachTest() { + loadListenerMock = mock(IUnityAdsLoadListener.class); + loadModuleMock = mock(ILoadModule.class); + } + + @After + public void afterEachTest() { + //Allow for any timeout threads to complete before starting the next test to prevent inaccurate mock counts + TestUtilities.SleepCurrentThread(loadTimeout); + } + + @Test + public void onUnityAdsFailedToLoadIsCalledWhenTimeoutIsReached() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock); + + timeoutDecorator.executeAdOperation(mock(IWebViewBridgeInvoker.class), loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, UnityAds.UnityAdsLoadError.TIMEOUT, "[UnityAds] Timeout while loading " + placementId); + } + + @Test + public void onUnityAdsAdLoadedAndOnUnityAdsAdFailedToLoadIsNotCalledAgainWhenTimeoutHasBeenReached() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock); + + timeoutDecorator.executeAdOperation(mock(IWebViewBridgeInvoker.class), loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + timeoutDecorator.onUnityAdsAdLoaded(placementId); + timeoutDecorator.onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, UnityAds.UnityAdsLoadError.TIMEOUT, "[UnityAds] Timeout while loading " + placementId); + Mockito.verify(loadListenerMock, times(0)).onUnityAdsAdLoaded(anyString()); + } + + @Test + public void onUnityAdsAdFailedToLoadIsNotCalledWhenOnUnityAdsAdLoadedIsCalledBeforeTimeout() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock); + + timeoutDecorator.executeAdOperation(mock(IWebViewBridgeInvoker.class), loadOperationState); + TestUtilities.SleepCurrentThread(25); + timeoutDecorator.onUnityAdsAdLoaded(placementId); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadModuleMock, times(1)).onUnityAdsAdLoaded(placementId); + Mockito.verify(loadModuleMock, times(0)).onUnityAdsFailedToLoad(anyString(), any(UnityAds.UnityAdsLoadError.class), anyString()); + } + + @Test + public void noNPEIsThrownWhenOnUnityAdsAdLoadedIsCalledWithoutCallingExecuteAdOperation() { + LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock); + timeoutDecorator.onUnityAdsAdLoaded(placementId); + Mockito.verify(loadModuleMock, times(0)).onUnityAdsFailedToLoad(anyString(), any(UnityAds.UnityAdsLoadError.class), anyString()); + } + + @Test + public void noNPEIsThrownWhenOnUnityAdsAdFailedToLoadIsCalledWithoutCallingExecuteAdOperation() { + LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock); + timeoutDecorator.onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage); + Mockito.verify(loadModuleMock, times(0)).onUnityAdsAdLoaded(anyString()); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleTests.java new file mode 100644 index 00000000..371cfb76 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadModuleTests.java @@ -0,0 +1,170 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.ads.IUnityAdsLoadListener; +import com.unity3d.ads.UnityAds; +import com.unity3d.ads.UnityAdsLoadOptions; +import com.unity3d.ads.test.TestUtilities; +import com.unity3d.services.ads.operation.load.LoadOperationState; +import com.unity3d.services.ads.operation.load.LoadModule; +import com.unity3d.services.ads.operation.load.LoadOperation; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.lang.reflect.Method; +import java.util.HashMap; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class LoadModuleTests { + private static String placementId = "TestPlacementId"; + private static UnityAds.UnityAdsLoadError loadError = UnityAds.UnityAdsLoadError.INTERNAL_ERROR; + private static String loadErrorMessage = "LoadErrorMessage"; + private static int uiThreadDelay = 50; + private static int webViewTimeout = 150; + private static UnityAdsLoadOptions unityAdsLoadOptions = new UnityAdsLoadOptions(); + + private IWebViewBridgeInvoker webViewBridgeInvokerMock; + private IUnityAdsLoadListener loadListenerMock; + private ISDKMetricSender sdkMetricSender; + private LoadModule loadModule; + + @Before + public void beforeEachTest() { + webViewBridgeInvokerMock = mock(IWebViewBridgeInvoker.class); + loadListenerMock = mock(IUnityAdsLoadListener.class); + sdkMetricSender = mock(ISDKMetricSender.class); + loadModule = new LoadModule(sdkMetricSender); + } + + @After + public void afterEachTest() { + //Allow for any timeout threads to complete before starting the next test to prevent inaccurate mock counts + TestUtilities.SleepCurrentThread(webViewTimeout); + } + + @Test + public void executeAdOperationCallsOnUnityAdsFailedToLoadWhenPlacementNotSet() { + LoadOperationState loadOperationState = new LoadOperationState(null, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + + loadModule.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(null, UnityAds.UnityAdsLoadError.INVALID_ARGUMENT,"[UnityAds] Placement ID cannot be null"); + } + + @Test + public void executeAdOperationCallsOnUnityAdsFailedToLoadWhenWebViewBridgeInvocationFails() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + //fail invocation + return false; + } + }).when(webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + loadModule.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, "[UnityAds] Internal communication failure"); + Mockito.verify(sdkMetricSender, times(1)).SendSDKMetricEventWithTag(SDKMetricEvents.native_load_callback_error, new HashMap (){{ + put("cbs", "invocationFailure"); + }}); + } + + @Test + public void executeAdOperationCallsOnUnityAdsFailedToLoadWhenWebViewBridgeInvocationTimesOut() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(50)); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + //Succeed invocation but don't simulate any return call from WebView so the request acknowledgement times out. + return true; + } + }).when(webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + loadModule.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); + TestUtilities.SleepCurrentThread(100); + TestUtilities.SleepCurrentThread(150); + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, "[UnityAds] Internal communication timeout"); + Mockito.verify(sdkMetricSender, times(1)).SendSDKMetricEvent(eq(SDKMetricEvents.native_load_callback_timeout)); + } + + @Test + public void executeAdOperationCallsOnUnityAdsFailedToLoadWhenJSONObjectThrowsException() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, null, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + + loadModule.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, "[UnityAds] Failed to create load request"); + } + + @Test + public void executeAdOperationCallsWebViewBridgeInvoker() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + + loadModule.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(webViewBridgeInvokerMock, times(1)).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + } + + @Test + public void onUnityAdsLoadedCallsLoadEventStateOnUnityAdsAdLoadedWhenSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + loadModule.set(loadOperation); + + loadModule.onUnityAdsAdLoaded(loadOperation.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(loadListenerMock, times(1)).onUnityAdsAdLoaded(placementId); + } + + @Test + public void onUnityAdsLoadedDoesNotCallLoadEventStateOnUnityAdsAdLoadedWhenLoadOperationIsNull() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + loadModule.set(null); + + loadModule.onUnityAdsAdLoaded(loadOperation.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + //No Verification as there is nothing to mock against. A null pointer exception is all that is being checked for here. + } + + @Test + public void onUnityAdsFailedToLoadCallsLoadEventStateOnUnityAdsFailedToLoadWhenSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + loadModule.set(loadOperation); + + loadModule.onUnityAdsFailedToLoad(loadOperation.getId(), loadError, loadErrorMessage); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage); + } + + @Test + public void onUnityAdsFailedToLoadDoesNotCallLoadEventStateOnUnityAdsFailedToLoadWhenLoadOperationIsNull() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, unityAdsLoadOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(webViewTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + loadModule.set(null); + + loadModule.onUnityAdsFailedToLoad(loadOperation.getId(), loadError, loadErrorMessage); + TestUtilities.SleepCurrentThread(uiThreadDelay); + //No Verification as there is nothing to mock against. A null pointer exception is all that is being checked for here. + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadOperationTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadOperationTests.java new file mode 100644 index 00000000..430aef70 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/LoadOperationTests.java @@ -0,0 +1,133 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.ads.IUnityAdsLoadListener; +import com.unity3d.ads.UnityAds; +import com.unity3d.ads.UnityAdsLoadOptions; +import com.unity3d.ads.test.TestUtilities; +import com.unity3d.services.ads.operation.load.LoadOperationState; +import com.unity3d.services.ads.operation.load.LoadOperation; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; + +import junit.framework.Assert; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class LoadOperationTests { + private static String placementId = "TestPlacementId"; + private static UnityAds.UnityAdsLoadError loadError = UnityAds.UnityAdsLoadError.INTERNAL_ERROR; + private static String loadErrorMessage = "LoadErrorMessage"; + private static UnityAdsLoadOptions loadOptions = new UnityAdsLoadOptions(); + + private static int loadTimeout = 150; + private static int uiThreadDelay = 25; + + private IUnityAdsLoadListener loadListenerMock; + + @Before + public void beforeEachTest() { + loadListenerMock = mock(IUnityAdsLoadListener.class); + } + + @After + public void afterEachTest() { + //Allow for any timeout threads to complete before starting the next test to prevent inaccurate mock counts + TestUtilities.SleepCurrentThread(loadTimeout); + } + + @Test + public void onUnityAdsAdLoadedCallsListenerOnUnityAdsAdLoadedWhenSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsAdLoaded(placementId); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsAdLoaded(placementId); + } + + @Test + public void onUnityAdsAdLoadedDoesNotCallListenerWhenListenerNotSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, null, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsAdLoaded(placementId); + TestUtilities.SleepCurrentThread(uiThreadDelay); + //No Verification as there is nothing to mock against. A null pointer exception is all that is being checked for here. + } + + @Test + public void onUnityAdsAdLoadedDoesNotCallListenerWhenLoadEventStateNotSet() { + LoadOperation loadOperation = new LoadOperation(null, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsAdLoaded(null); + TestUtilities.SleepCurrentThread(uiThreadDelay); + //No Verification as there is nothing to mock against. A null pointer exception is all that is being checked for here. + } + + @Test + public void onUnityAdsAdLoadedDoesNotCallListenerWhenPlacementIdNotSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsAdLoaded(null); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(0)).onUnityAdsAdLoaded(anyString()); + } + + @Test + public void onUnityAdsFailedToLoadCallsListenerOnUnityAdsFailedToLoadWhenSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage); + } + + @Test + public void onUnityAdsFailedToLoadDoesNotCallListenerWhenListenerNotSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, null, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage); + TestUtilities.SleepCurrentThread(uiThreadDelay); + //No Verification as there is nothing to mock against. A null pointer exception is all that is being checked for here. + } + + @Test + public void onUnityAdsFailedToLoadDoesNotCallListenerWhenLoadEventStateNotSet() { + LoadOperation loadOperation = new LoadOperation(null, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsFailedToLoad(null, loadError, loadErrorMessage); + TestUtilities.SleepCurrentThread(uiThreadDelay); + //No Verification as there is nothing to mock against. A null pointer exception is all that is being checked for here. + } + + @Test + public void onUnityAdsFailedToLoadDoesNotCallListenerWhenPlacementIdNotSet() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + + loadOperation.onUnityAdsFailedToLoad(null, loadError, loadErrorMessage); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(loadListenerMock, times(0)).onUnityAdsFailedToLoad(null, loadError, loadErrorMessage); + } + + @Test + public void getLoadEventState() { + LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout)); + LoadOperation loadOperation = new LoadOperation(loadOperationState, mock(IWebViewBridgeInvocation.class)); + Assert.assertEquals("LoadEventState object should match", loadOperationState, loadOperation.getLoadOperationState()); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/OperationTestUtilities.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/OperationTestUtilities.java new file mode 100644 index 00000000..6566b337 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/OperationTestUtilities.java @@ -0,0 +1,51 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import com.unity3d.services.core.configuration.Configuration; + +import org.json.JSONObject; + +public class OperationTestUtilities { + static Configuration createConfigurationWithWebviewTimeout(int timeoutLengthInMilliseconds) { + Configuration configuration = null; + JSONObject json = new JSONObject(); + try { + json.put("url", "fake-url"); + json.put("hash", "fake-hash"); + json.put("version", "fake-version"); + json.put("wto", timeoutLengthInMilliseconds); + configuration = new Configuration(json); + } catch (Exception e) { + } + + return configuration; + } + static Configuration createConfigurationWithLoadTimeout(int timeoutLengthInMilliseconds) { + Configuration configuration = null; + JSONObject json = new JSONObject(); + try { + json.put("url", "fake-url"); + json.put("hash", "fake-hash"); + json.put("version", "fake-version"); + json.put("lto", timeoutLengthInMilliseconds); + configuration = new Configuration(json); + } catch (Exception e) { + } + + return configuration; + } + + static Configuration createConfigurationWithShowTimeout(int timeoutLengthInMilliseconds) { + Configuration configuration = null; + JSONObject json = new JSONObject(); + try { + json.put("url", "fake-url"); + json.put("hash", "fake-hash"); + json.put("version", "fake-version"); + json.put("sto", timeoutLengthInMilliseconds); + configuration = new Configuration(json); + } catch (Exception e) { + } + + return configuration; + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleTests.java new file mode 100644 index 00000000..639d7c92 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleTests.java @@ -0,0 +1,207 @@ +package com.unity3d.ads.test.instrumentation.services.ads.operation; + +import androidx.test.rule.ActivityTestRule; + +import com.unity3d.ads.IUnityAdsShowListener; +import com.unity3d.ads.UnityAds; +import com.unity3d.ads.UnityAdsShowOptions; +import com.unity3d.ads.test.TestUtilities; +import com.unity3d.ads.test.instrumentation.InstrumentationTestActivity; +import com.unity3d.services.ads.operation.show.IShowModule; +import com.unity3d.services.ads.operation.show.ShowModule; +import com.unity3d.services.ads.operation.show.ShowOperation; +import com.unity3d.services.ads.operation.show.ShowOperationState; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.lang.reflect.Method; +import java.util.HashMap; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class ShowModuleTests { + private static String placementId = "TestPlacementId"; + private static UnityAdsShowOptions showOptions = new UnityAdsShowOptions(); + + private static int showTimeout = 100; + private static int uiThreadDelay = 25; + + private IUnityAdsShowListener _showListenerMock; + private IShowModule _showModule; + private ISDKMetricSender _sdkMetricSender; + private IWebViewBridgeInvoker _webViewBridgeInvokerMock; + + @Rule + public final ActivityTestRule _activityRule = new ActivityTestRule<>(InstrumentationTestActivity.class); + + @Before + public void beforeEachTest() { + _showListenerMock = mock(IUnityAdsShowListener.class); + _webViewBridgeInvokerMock = mock(IWebViewBridgeInvoker.class); + _sdkMetricSender = mock(ISDKMetricSender.class); + _showModule = new ShowModule(_sdkMetricSender); + } + + @Test + public void executeAdOperationCallsOnUnityAdsFailedToShowWhenPlacementNotSet() { + ShowOperationState showOperationState = new ShowOperationState(null, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(null, UnityAds.UnityAdsShowError.INVALID_ARGUMENT, "[UnityAds] Placement ID cannot be null"); + } + + @Test + public void showModuleCallsOnUnityAdsFailedToShowWhenInvocationCallbackFails() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + return false; + } + }).when(_webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "WebViewBridgeInvocation:execute: invokeMethod failure"); + Mockito.verify(_sdkMetricSender, times(1)).SendSDKMetricEventWithTag(SDKMetricEvents.native_show_callback_error, new HashMap (){{ + put("cbs", "invocationFailure"); + }}); + } + + @Test + public void showModuleCallsOnUnityAdsFailedToShowWhenInvocationCallbackTimesOut() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithWebviewTimeout(50)); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + //Succeed invocation but don't simulate any return call from WebView so the request acknowledgement times out. + return true; + } + }).when(_webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); + TestUtilities.SleepCurrentThread(250); + + Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "[UnityAds] Show Invocation Timeout"); + Mockito.verify(_sdkMetricSender, times(1)).SendSDKMetricEvent(eq(SDKMetricEvents.native_show_callback_timeout)); + } + + @Test + public void showModuleCallsOnUnityAdsFailedToShowWhenShowOptionsThrowsNPE() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), null, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); + + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "[UnityAds] Error creating show options"); + } + + @Test + public void showModuleCallsWebViewBridgeInvoker() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + + _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify((_webViewBridgeInvokerMock), times(1)).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + } + + @Test + public void onUnityAdsShowFailureCallsListenerOnUnityAdsShowFailure() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.set(showOperation); + _showModule.onUnityAdsShowFailure(showOperation.getId(), UnityAds.UnityAdsShowError.INTERNAL_ERROR, "ErrorMessage"); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(_showListenerMock, times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "ErrorMessage"); + } + + @Test + public void onUnityAdsShowFailureDoesNothingWhenOperationCannotBeFound() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.onUnityAdsShowFailure(showOperation.getId(), UnityAds.UnityAdsShowError.INTERNAL_ERROR, "ErrorMessage"); + TestUtilities.SleepCurrentThread(uiThreadDelay); + } + + @Test + public void onUnityAdsShowStartCallsListenerOnUnityAdsShowStart() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.set(showOperation); + _showModule.onUnityAdsShowStart(showOperation.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(_showListenerMock, times(1)).onUnityAdsShowStart(placementId); + } + + @Test + public void onUnityAdsShowStartDoesNothingWhenOperationCannotBeFound() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.onUnityAdsShowStart(showOperation.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + } + + @Test + public void onUnityAdsShowClickCallsListenerOnUnityAdsShowClick() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.set(showOperation); + _showModule.onUnityAdsShowClick(showOperation.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(_showListenerMock, times(1)).onUnityAdsShowClick(placementId); + } + + @Test + public void onUnityAdsShowClickDoesNothingWhenOperationCannotBeFound() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.onUnityAdsShowClick(showOperation.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + } + + @Test + public void onUnityAdsShowCompleteCallsListenerOnUnityAdsShowComplete() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.set(showOperation); + _showModule.onUnityAdsShowComplete(showOperation.getId(), UnityAds.UnityAdsShowCompletionState.COMPLETED); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + Mockito.verify(_showListenerMock, times(1)).onUnityAdsShowComplete(placementId, UnityAds.UnityAdsShowCompletionState.COMPLETED); + } + + @Test + public void onUnityAdsShowCompleteDoesNothingWhenOperationCannotBeFound() { + ShowOperationState showOperationState = new ShowOperationState(placementId, _showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + ShowOperation showOperation = new ShowOperation(showOperationState, mock(IWebViewBridgeInvocation.class)); + + _showModule.onUnityAdsShowComplete(showOperation.getId(), UnityAds.UnityAdsShowCompletionState.COMPLETED); + TestUtilities.SleepCurrentThread(uiThreadDelay); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/properties/AdsPropertiesTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/properties/AdsPropertiesTests.java index 4bb2b2bd..9aec0598 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/properties/AdsPropertiesTests.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/properties/AdsPropertiesTests.java @@ -1,6 +1,6 @@ package com.unity3d.ads.test.instrumentation.services.ads.properties; -import android.support.test.rule.ActivityTestRule; +import androidx.test.rule.ActivityTestRule; import com.unity3d.ads.IUnityAdsListener; import com.unity3d.ads.UnityAds; diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/webplayer/WebPlayerViewCacheTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/webplayer/WebPlayerViewCacheTest.java index 5be8a099..adc074d5 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/webplayer/WebPlayerViewCacheTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/webplayer/WebPlayerViewCacheTest.java @@ -1,9 +1,9 @@ package com.unity3d.ads.test.instrumentation.services.ads.webplayer; import android.app.Activity; -import android.support.test.annotation.UiThreadTest; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.annotation.UiThreadTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.ads.test.legacy.TestActivity; import com.unity3d.services.ads.webplayer.WebPlayerView; diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/banners/BannerViewCacheTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/banners/BannerViewCacheTests.java index d3326fb5..653202de 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/banners/BannerViewCacheTests.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/banners/BannerViewCacheTests.java @@ -1,7 +1,7 @@ package com.unity3d.ads.test.instrumentation.services.banners; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.ads.test.instrumentation.InstrumentationTestActivity; import com.unity3d.services.banners.BannerErrorCode; diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/configuration/InitializationNotificationCenterTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/configuration/InitializationNotificationCenterTest.java index 466b9077..5ddbc321 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/configuration/InitializationNotificationCenterTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/configuration/InitializationNotificationCenterTest.java @@ -11,8 +11,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import static org.junit.Assert.assertEquals; diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/WebViewBridgeSharedObjectTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/WebViewBridgeSharedObjectTests.java new file mode 100644 index 00000000..6c39cc45 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/WebViewBridgeSharedObjectTests.java @@ -0,0 +1,83 @@ +package com.unity3d.ads.test.instrumentation.services.core.webview.bridge; + +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; +import com.unity3d.services.core.webview.bridge.WebViewBridgeSharedObjectStore; + +import org.hamcrest.core.Is; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +public class WebViewBridgeSharedObjectTests { + //Test Implementation of WebViewBridgeSharedObjectStore + private class WebViewBridgeSharedObjectStoreTestImplementation extends WebViewBridgeSharedObjectStore { + + @Override + public synchronized IWebViewSharedObject get(String id) { + return super.get(id); + } + + @Override + public synchronized void set(IWebViewSharedObject sharedObject) { + super.set(sharedObject); + } + } + + private static String TestId = "TestId"; + + private WebViewBridgeSharedObjectStoreTestImplementation sharedObjectStore; + private IWebViewSharedObject sharedObject; + + @Before + public void beforeEachTest() { + sharedObjectStore = new WebViewBridgeSharedObjectStoreTestImplementation(); + sharedObject = mock(IWebViewSharedObject.class); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + return TestId; + }}).when(sharedObject).getId(); + } + + @Test + public void testGetReturnsObjectStoredInHashMapBySet() { + sharedObjectStore.set(sharedObject); + Assert.assertThat("Expected object was not returned by get", sharedObjectStore.get(sharedObject.getId()), Is.is(sharedObject)); + } + + @Test + public void testGetReturnsNullWhenObjectDoesNotExist() { + Assert.assertNull(sharedObjectStore.get(sharedObject.getId())); + } + + @Test + public void testSetWithNullObject() { + sharedObjectStore.set(null); + } + + @Test + public void testGetWithNullObject() { + Assert.assertNull(sharedObjectStore.get(null)); + } + + @Test + public void testGetReturnsNullAfterRemove() { + sharedObjectStore.set(sharedObject); + Assert.assertThat("Shared object was not stored properly", sharedObjectStore.get(sharedObject.getId()), Is.is(sharedObject)); + sharedObjectStore.remove(sharedObject.getId()); + Assert.assertNull("Shared object should have been removed", sharedObjectStore.get(sharedObject.getId())); + } + + @Test + public void testRemoveDoesNothingIfSharedObjectIsNull() { + sharedObjectStore.remove((IWebViewSharedObject) null); + } + + @Test + public void testRemoveDoesNothingIfSharedObjectIdDoesNotExist() { + sharedObjectStore.remove(sharedObject.getId()); + } +} diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationTests.java new file mode 100644 index 00000000..1c298233 --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationTests.java @@ -0,0 +1,133 @@ +package com.unity3d.ads.test.instrumentation.services.core.webview.bridge.invocation; + +import com.unity3d.ads.test.TestUtilities; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocationCallback; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocation; +import com.unity3d.services.core.webview.bridge.CallbackStatus; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.lang.reflect.Method; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class WebViewBridgeInvocationTests { + private static String className = "ClassName"; + private static String methodName = "MethodName"; + private static int invocationTimeout = 100; + private static Object params = new Object(); + private static int uiThreadDelay = 10; + + private IWebViewBridgeInvoker webViewBridgeInvokerMock; + private IWebViewBridgeInvocationCallback invocationCallbackMock; + + @Before + public void beforeEachTest() { + webViewBridgeInvokerMock = mock(IWebViewBridgeInvoker.class); + invocationCallbackMock = mock(IWebViewBridgeInvocationCallback.class); + } + + @After + public void afterEachTest() { + TestUtilities.SleepCurrentThread(invocationTimeout); + } + + @Test + public void invokeCallsOnSuccessCallbackWhenInvocationCompleteOk() { + final WebViewBridgeInvocation webViewBridgeInvocation = new WebViewBridgeInvocation(webViewBridgeInvokerMock, invocationCallbackMock); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + webViewBridgeInvocation.onInvocationComplete(CallbackStatus.OK); + return true; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(),anyString(), any(Method.class),any(Object.class)); + webViewBridgeInvocation.invoke(className, methodName, invocationTimeout, params); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 1, 0, 0); + } + + @Test + public void invokeCallsOnFailureCallbackWhenInvocationCompleteError() { + final WebViewBridgeInvocation webViewBridgeInvocation = new WebViewBridgeInvocation(webViewBridgeInvokerMock, invocationCallbackMock); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + webViewBridgeInvocation.onInvocationComplete(CallbackStatus.ERROR); + return true; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(),anyString(), any(Method.class),any(Object.class)); + webViewBridgeInvocation.invoke(className, methodName, invocationTimeout, params); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 0,1,0, "WebViewBridgeInvocation:OnInvocationComplete: CallbackStatus.Error", CallbackStatus.ERROR); + } + + @Test + public void invokeCallsOnTimeoutCallbackWhenNoInvocationResponse() { + WebViewBridgeInvocation webViewBridgeInvocation = new WebViewBridgeInvocation(webViewBridgeInvokerMock, invocationCallbackMock); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + return true; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(),anyString(), any(Method.class),any(Object.class)); + webViewBridgeInvocation.invoke(className, methodName, 100, params); + TestUtilities.SleepCurrentThread(250); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 0,0,1); + } + + @Test(expected = IllegalArgumentException.class) + public void WebViewBridgeInvocationThrowsNPEWhenInvokedWithNullWebViewBridgeInvoker() { + new WebViewBridgeInvocation(null, mock(IWebViewBridgeInvocationCallback.class)); + } + + @Test + public void NoErrorIsThrownWhenWebViewBridgeInvocationIsInitializedWithNullCallbackAndInvokeMethodIsCalledAndFails() { + final WebViewBridgeInvocation webViewBridgeInvocation = new WebViewBridgeInvocation(webViewBridgeInvokerMock, null); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + webViewBridgeInvocation.onInvocationComplete(CallbackStatus.ERROR); + return false; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(),anyString(), any(Method.class),any(Object.class)); + webViewBridgeInvocation.invoke(className, methodName, invocationTimeout, params); + TestUtilities.SleepCurrentThread(uiThreadDelay); + } + + @Test + public void invokeCallsOnFailureCallbackWhenInvokerFails() { + IWebViewBridgeInvocationCallback invocationCallbackMock = mock(IWebViewBridgeInvocationCallback.class); + final WebViewBridgeInvocation webViewBridgeInvocation = new WebViewBridgeInvocation(webViewBridgeInvokerMock, invocationCallbackMock); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + return false; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(),anyString(), any(Method.class),any(Object.class)); + webViewBridgeInvocation.invoke(className, methodName, invocationTimeout, params); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 0,1,0, "WebViewBridgeInvocation:execute: invokeMethod failure", null); + } + + private void VerifyInvocationCallbackMockCalls(IWebViewBridgeInvocationCallback webViewBridgeInvocationCallback, int onSuccessCalls, int onFailureCalls, int onTimeoutCalls, String onFailureMessage, CallbackStatus callbackStatus) { + Mockito.verify(webViewBridgeInvocationCallback, times(onSuccessCalls)).onSuccess(); + Mockito.verify(webViewBridgeInvocationCallback, times(onFailureCalls)).onFailure(onFailureMessage, callbackStatus); + Mockito.verify(webViewBridgeInvocationCallback, times(onTimeoutCalls)).onTimeout(); + } + + private void VerifyInvocationCallbackMockCalls(IWebViewBridgeInvocationCallback webViewBridgeInvocationCallback, int onSuccessCalls, int onFailureCalls, int onTimeoutCalls) { + Mockito.verify(webViewBridgeInvocationCallback, times(onSuccessCalls)).onSuccess(); + Mockito.verify(webViewBridgeInvocationCallback, times(onFailureCalls)).onFailure(anyString(), any(CallbackStatus.class)); + Mockito.verify(webViewBridgeInvocationCallback, times(onTimeoutCalls)).onTimeout(); + } +} \ No newline at end of file diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/integration/banner/BannerIntegrationTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/integration/banner/BannerIntegrationTest.java index 97fe9ca5..52476732 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/integration/banner/BannerIntegrationTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/integration/banner/BannerIntegrationTest.java @@ -1,9 +1,8 @@ package com.unity3d.ads.test.integration.banner; -import android.support.test.rule.ActivityTestRule; +import androidx.test.rule.ActivityTestRule; import android.view.View; -import com.unity3d.ads.IUnityAdsListener; import com.unity3d.ads.UnityAds; import com.unity3d.ads.test.hybrid.HybridTestActivity; import com.unity3d.services.banners.BannerErrorInfo; @@ -19,7 +18,6 @@ import org.junit.BeforeClass; import org.junit.ClassRule; -import org.junit.Rule; import org.junit.Test; import java.util.concurrent.Semaphore; diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTest.java index f5c6f5f2..cdc1f2b2 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTest.java @@ -4,7 +4,7 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.Looper; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.ads.adunit.AdUnitActivity; import com.unity3d.services.ads.adunit.AdUnitEvent; @@ -23,6 +23,9 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4.class) public class AdUnitActivityTest extends AdUnitActivityTestBaseClass { diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTestBaseClass.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTestBaseClass.java index 559ef6d9..35c8b8aa 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTestBaseClass.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdUnitActivityTestBaseClass.java @@ -5,9 +5,8 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.Looper; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.test.ActivityInstrumentationTestCase2; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.ads.adunit.AdUnitActivity; import com.unity3d.services.core.configuration.Configuration; @@ -17,10 +16,8 @@ import com.unity3d.services.core.webview.WebViewApp; import com.unity3d.services.core.webview.bridge.CallbackStatus; import com.unity3d.services.core.webview.bridge.Invocation; -import android.support.test.rule.ActivityTestRule; +import androidx.test.rule.ActivityTestRule; -import org.junit.After; -import org.junit.Before; import org.junit.Rule; import org.junit.runner.RunWith; @@ -30,7 +27,7 @@ import java.util.List; @RunWith(AndroidJUnit4.class) -public class AdUnitActivityTestBaseClass extends ActivityInstrumentationTestCase2 { +public class AdUnitActivityTestBaseClass extends ActivityTestRule { public AdUnitActivityTestBaseClass() { super(AdUnitActivity.class); } @@ -49,16 +46,6 @@ protected void afterActivityFinished() { @Rule public MyCustomRule testRule = new MyCustomRule<>(AdUnitActivity.class, false, false); - @Before - public void setUp() throws Exception { - super.setUp(); - injectInstrumentation(InstrumentationRegistry.getInstrumentation()); - } - - @After - public void tearDown() throws Exception { - super.tearDown(); - } protected class MockWebViewApp extends WebViewApp { public CallbackStatus CALLBACK_STATUS = null; @@ -197,7 +184,7 @@ public boolean sendEvent(Enum eventCategory, Enum eventId, Object... params) { handler.post(new Runnable() { @Override public void run() { - WebViewApp.getCurrentApp().setWebView(new WebView(InstrumentationRegistry.getTargetContext())); + WebViewApp.getCurrentApp().setWebView(new WebView(InstrumentationRegistry.getInstrumentation().getTargetContext())); webViewCV.open(); } }); diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdvertisingIdentifierTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdvertisingIdentifierTest.java index 3894d9d0..c14d0db6 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdvertisingIdentifierTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/AdvertisingIdentifierTest.java @@ -1,7 +1,7 @@ package com.unity3d.ads.test.legacy; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.core.device.AdvertisingId; @@ -18,7 +18,7 @@ public class AdvertisingIdentifierTest { @BeforeClass public static void setupAdvertisingIdentifier() { - AdvertisingId.init(InstrumentationRegistry.getTargetContext()); + AdvertisingId.init(InstrumentationRegistry.getInstrumentation().getTargetContext()); } @Test diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/BroadcastTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/BroadcastTest.java index 005a0a6f..7f07741c 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/BroadcastTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/BroadcastTest.java @@ -3,8 +3,8 @@ import android.content.Intent; import android.net.Uri; import android.os.ConditionVariable; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.core.broadcast.BroadcastEvent; import com.unity3d.services.core.broadcast.BroadcastEventReceiver; @@ -30,7 +30,7 @@ public class BroadcastTest { @Before public void setup() { - ClientProperties.setApplicationContext(InstrumentationRegistry.getTargetContext()); + ClientProperties.setApplicationContext(InstrumentationRegistry.getInstrumentation().getTargetContext()); } @After diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/CacheTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/CacheTest.java index b2a26f79..cc2db3f6 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/CacheTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/CacheTest.java @@ -1,8 +1,8 @@ package com.unity3d.ads.test.legacy; import android.os.ConditionVariable; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.ads.adunit.AdUnitEvent; import com.unity3d.services.core.api.Cache; @@ -58,7 +58,7 @@ public class CacheTest { @Before public void setup() throws IOException { - ClientProperties.setApplicationContext(InstrumentationRegistry.getTargetContext()); + ClientProperties.setApplicationContext(InstrumentationRegistry.getInstrumentation().getTargetContext()); assertNotEquals("Cache directory has not been properly initialized", null, SdkProperties.getCacheDirectory()); // Write test file to cache directory diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ClientPropertiesTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ClientPropertiesTest.java index 81b3a5f6..df22a3dd 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ClientPropertiesTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ClientPropertiesTest.java @@ -4,8 +4,8 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.Looper; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.ads.IUnityAdsListener; import com.unity3d.ads.UnityAds; @@ -27,7 +27,7 @@ public class ClientPropertiesTest { @BeforeClass public static void prepareTests () { - ClientProperties.setApplicationContext(InstrumentationRegistry.getTargetContext()); + ClientProperties.setApplicationContext(InstrumentationRegistry.getInstrumentation().getTargetContext()); } @Before @@ -46,8 +46,8 @@ private void removeListeners() { @Test public void testSetApplicationContext () { - ClientProperties.setApplicationContext(InstrumentationRegistry.getTargetContext()); - assertEquals("Application context was not the same as expected", InstrumentationRegistry.getTargetContext(), ClientProperties.getApplicationContext()); + ClientProperties.setApplicationContext(InstrumentationRegistry.getInstrumentation().getTargetContext()); + assertEquals("Application context was not the same as expected", InstrumentationRegistry.getInstrumentation().getTargetContext(), ClientProperties.getApplicationContext()); } @Test diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConfigurationTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConfigurationTest.java index bda2bee1..dd300f59 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConfigurationTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConfigurationTest.java @@ -1,13 +1,12 @@ package com.unity3d.ads.test.legacy; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.core.configuration.Configuration; import org.json.JSONObject; import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,7 +27,7 @@ private JSONObject getAllConfigDataJSON(String webViewUrl, String hash, String v boolean delayUpdate, int resetTimeout, int maxRetries, long retryDelay, double scalingFactor, int connectEventThreshold, int maxConnectedEvents, long networkErrorTimeout, int showTimeout, - int loadTimeout, int noFillTimeout, String sdkVersion, String metricsUrl, + int loadTimeout, int webViewBridgeTimeout, String sdkVersion, String metricsUrl, double metricSampleRate, long webviewCreateTimeout) { JSONObject json = new JSONObject(); @@ -46,7 +45,7 @@ private JSONObject getAllConfigDataJSON(String webViewUrl, String hash, String v json.put("net", networkErrorTimeout); json.put("sto", showTimeout); json.put("lto", loadTimeout); - json.put("nft", noFillTimeout); + json.put("wto", webViewBridgeTimeout); json.put("sdkv", sdkVersion); json.put("murl", metricsUrl); json.put("msr", metricSampleRate); @@ -79,9 +78,9 @@ public void testInitForOptionalParameterFallback() { assertEquals("Connected Event threshold does not equal what is expected", 10000, config.getConnectedEventThreshold()); assertEquals("Maximum Connected Events does not equal what is expected", 500, config.getMaximumConnectedEvents()); assertEquals("Network Error Timeout does not equal what is expected", 60000L, config.getNetworkErrorTimeout()); - assertEquals("Show Timeout does not equal what is expected", 5000, config.getShowTimeout()); - assertEquals("Load Timeout does not equal what is expected", 5000, config.getLoadTimeout()); - assertEquals("No fill Timeout does not equal what is expected",30000, config.getNoFillTimeout()); + assertEquals("Show Timeout does not equal what is expected", 10000, config.getShowTimeout()); + assertEquals("Load Timeout does not equal what is expected", 30000, config.getLoadTimeout()); + assertEquals("WebViewBridge Timeout does not equal what is expected",5000, config.getWebViewBridgeTimeout()); assertEquals("SDK Version should not be set", "", config.getSdkVersion()); assertEquals("Metric URL does not equal what is expected", "", config.getMetricsUrl()); assertEquals("Metric Sample Rate does not equal what is expected", 100, config.getMetricSampleRate(), 0); @@ -133,7 +132,7 @@ public void testInitWithAllOptionalParameters() { assertEquals("Network Error Timeout does not equal what is expected", networkErrorTimeout, config.getNetworkErrorTimeout()); assertEquals("Show Timeout does not equal what is expected", showTimeout, config.getShowTimeout()); assertEquals("Load Timeout does not equal what is expected", loadTimeout, config.getLoadTimeout()); - assertEquals("No fill Timeout does not equal what is expected", noFillTimeout, config.getNoFillTimeout()); + assertEquals("No fill Timeout does not equal what is expected", noFillTimeout, config.getWebViewBridgeTimeout()); assertEquals("SDK Version does not equal what is expected", sdkVersion, config.getSdkVersion()); assertEquals("Metrics URL does not equal what is expected", metricsUrl, config.getMetricsUrl()); assertEquals("Metrics Sample Rate does not equal what is expected", metricSampleRate, config.getMetricSampleRate(), 0); diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConnectivityTest.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConnectivityTest.java index 331d3a7c..790235cb 100644 --- a/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConnectivityTest.java +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/legacy/ConnectivityTest.java @@ -3,8 +3,8 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.Looper; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.unity3d.services.core.api.Connectivity; import com.unity3d.services.core.connectivity.ConnectivityEvent; @@ -32,9 +32,11 @@ @RunWith(AndroidJUnit4.class) public class ConnectivityTest { + private boolean StopBackgroundTheads = false; + @Before public void setup() { - ClientProperties.setApplicationContext(InstrumentationRegistry.getTargetContext()); + ClientProperties.setApplicationContext(InstrumentationRegistry.getInstrumentation().getTargetContext()); } @Test @@ -45,19 +47,13 @@ public void testListener() { Listener listener = new Listener(); ConnectivityMonitor.addListener(listener); + int currentDisconnects = listener.getOnDisconnectedCalls(); ConnectivityMonitor.disconnected(); - assertEquals("ConnectivityMonitor disconnected callbacks not equal to one", 1, listener.getOnDisconnectedCalls()); + assertEquals("ConnectivityMonitor disconnected callbacks not equal to one", currentDisconnects+1, listener.getOnDisconnectedCalls()); + int currentConnects = listener.getOnConnectedCalls(); ConnectivityMonitor.connected(); - assertEquals("ConnectivityMonitor connected callbacks not equal to one", 1, listener.getOnConnectedCalls()); - } - - private List createListeners(int count) { - final List listeners = new ArrayList<>(); - for (int x=0; x listeners, final int iterations, final boolean add) { @@ -65,6 +61,9 @@ private Thread createListenerThread(final List listeners, final int it @Override public void run() { for (int x=0; x listeners = createListeners(10); @@ -104,6 +104,14 @@ public void testAddRemoveListenerThreadSafety() { Assert.assertThat(success, is(true)); } + private List createListeners(int count) { + final List listeners = new ArrayList<>(); + for (int x=0; x= 13) { - Point displaySize = new Point(); - defaultDisplay.getSize(displaySize); - display.put("width", displaySize.x); - display.put("height", displaySize.y); - } else { - display.put("width", defaultDisplay.getWidth()); - display.put("height", defaultDisplay.getHeight()); - } - options.put("display", display); - options.put("options", showOptions.getData()); - } catch(JSONException e) { - DeviceLog.exception("JSON error while constructing show options", e); - } - - try { - if(!AdUnitOpen.open(placementId, options)) { - handleShowError(placementId, UnityAds.UnityAdsError.INTERNAL_ERROR, "Webapp timeout, shutting down Unity Ads"); - } - } - catch (NoSuchMethodException exception) { - DeviceLog.exception("Could not get callback method", exception); - handleShowError(placementId, UnityAds.UnityAdsError.SHOW_ERROR, "Could not get com.unity3d.ads.properties.showCallback method"); - } - } - }); - } else { - if (!isSupported()) { - handleShowError(placementId, UnityAds.UnityAdsError.NOT_INITIALIZED, "Unity Ads is not supported for this device"); - } else if(!isInitialized()) { - handleShowError(placementId, UnityAds.UnityAdsError.NOT_INITIALIZED, "Unity Ads is not initialized"); - } else { - handleShowError(placementId, UnityAds.UnityAdsError.SHOW_ERROR, "Placement \"" + placementId + "\" is not ready"); - } - } + private static void handleShowError(IUnityAdsShowListener showListener, String placementId, UnityAds.UnityAdsShowError error, String message) { + if (showListener == null) return; + showListener.onUnityAdsShowFailure(placementId, error, message); } - private static void handleShowError(final String placementId, final UnityAds.UnityAdsError error, final String message) { + private static void handleLegacyListenerShowError(final String placementId, final UnityAds.UnityAdsError error, final String message) { final String errorMessage = "Unity Ads show failed: " + message; DeviceLog.error(errorMessage); @@ -324,15 +309,16 @@ public static boolean getDebugMode() { return UnityServices.getDebugMode(); } - public static String getDefaultPlacement() { - return Placement.getDefaultPlacement(); - } - public static void load(final String placementId, final UnityAdsLoadOptions loadOptions, final IUnityAdsLoadListener listener) { - LoadModule.getInstance().load(placementId, loadOptions, listener); + Configuration config = configuration == null ? new Configuration() : configuration; + LoadModule.getInstance().executeAdOperation(webViewBridgeInvoker, new LoadOperationState(placementId, listener, loadOptions, config)); } public static String getToken() { return TokenStorage.getToken(); } + + public static void setConfiguration(Configuration configuration) { + UnityAdsImplementation.configuration = configuration; + } } diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/api/Load.java b/unity-ads/src/main/java/com/unity3d/services/ads/api/Load.java index dd937a56..eaf68afe 100644 --- a/unity-ads/src/main/java/com/unity3d/services/ads/api/Load.java +++ b/unity-ads/src/main/java/com/unity3d/services/ads/api/Load.java @@ -1,19 +1,20 @@ package com.unity3d.services.ads.api; -import com.unity3d.services.ads.load.LoadModule; +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.load.LoadModule; import com.unity3d.services.core.webview.bridge.WebViewCallback; import com.unity3d.services.core.webview.bridge.WebViewExposed; public class Load { @WebViewExposed public static void sendAdLoaded(final String placementId, final String listenerId, WebViewCallback callback) { - LoadModule.getInstance().sendAdLoaded(placementId, listenerId); + LoadModule.getInstance().onUnityAdsAdLoaded(listenerId); callback.invoke(); } @WebViewExposed - public static void sendAdFailedToLoad(final String placementId, final String listenerId, WebViewCallback callback) { - LoadModule.getInstance().sendAdFailedToLoad(placementId, listenerId); + public static void sendAdFailedToLoad(final String placementId, final String listenerId, final String error, final String message, WebViewCallback callback) { + LoadModule.getInstance().onUnityAdsFailedToLoad(listenerId, UnityAds.UnityAdsLoadError.valueOf(error), message); callback.invoke(); } diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/api/Show.java b/unity-ads/src/main/java/com/unity3d/services/ads/api/Show.java new file mode 100644 index 00000000..1044e20b --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/api/Show.java @@ -0,0 +1,33 @@ + +package com.unity3d.services.ads.api; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.show.ShowModule; +import com.unity3d.services.core.webview.bridge.WebViewCallback; +import com.unity3d.services.core.webview.bridge.WebViewExposed; + +public class Show { + @WebViewExposed + public static void sendShowFailedEvent(final String placementId, final String listenerId, final String error, final String message, WebViewCallback callback) { + ShowModule.getInstance().onUnityAdsShowFailure(listenerId, UnityAds.UnityAdsShowError.valueOf(error), message); + callback.invoke(); + } + + @WebViewExposed + public static void sendShowStartEvent(final String placementId, final String listenerId, WebViewCallback callback) { + ShowModule.getInstance().onUnityAdsShowStart(listenerId); + callback.invoke(); + } + + @WebViewExposed + public static void sendShowClickEvent(final String placementId, final String listenerId, WebViewCallback callback) { + ShowModule.getInstance().onUnityAdsShowClick(listenerId); + callback.invoke(); + } + + @WebViewExposed + public static void sendShowCompleteEvent(final String placementId, final String listenerId, final String finishState, WebViewCallback callback) { + ShowModule.getInstance().onUnityAdsShowComplete(listenerId, UnityAds.UnityAdsShowCompletionState.valueOf(finishState)); + callback.invoke(); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/configuration/AdsModuleConfiguration.java b/unity-ads/src/main/java/com/unity3d/services/ads/configuration/AdsModuleConfiguration.java index aa32f034..60207ba1 100644 --- a/unity-ads/src/main/java/com/unity3d/services/ads/configuration/AdsModuleConfiguration.java +++ b/unity-ads/src/main/java/com/unity3d/services/ads/configuration/AdsModuleConfiguration.java @@ -4,8 +4,8 @@ import com.unity3d.ads.IUnityAdsListener; import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.UnityAdsImplementation; import com.unity3d.services.ads.adunit.AdUnitOpen; -import com.unity3d.services.ads.load.LoadModule; import com.unity3d.services.ads.placement.Placement; import com.unity3d.ads.properties.AdsProperties; import com.unity3d.services.ads.token.TokenStorage; @@ -31,6 +31,7 @@ public Class[] getWebAppApiClassList() { com.unity3d.services.ads.api.WebPlayer.class, com.unity3d.services.ads.api.Purchasing.class, com.unity3d.services.ads.api.Load.class, + com.unity3d.services.ads.api.Show.class, com.unity3d.services.ads.api.Token.class }; @@ -40,7 +41,7 @@ public Class[] getWebAppApiClassList() { public boolean resetState(Configuration configuration) { Placement.reset(); AdUnitOpen.setConfiguration(configuration); - LoadModule.setConfiguration(configuration); + UnityAdsImplementation.setConfiguration(configuration); TokenStorage.deleteTokens(); return true; } @@ -77,7 +78,7 @@ public void run() { return false; } AdUnitOpen.setConfiguration(configuration); - LoadModule.setConfiguration(configuration); + UnityAdsImplementation.setConfiguration(configuration); return true; } @@ -97,7 +98,7 @@ public void run() { public boolean initCompleteState(Configuration configuration) { AdUnitOpen.setConfiguration(configuration); - LoadModule.setConfiguration(configuration); + UnityAdsImplementation.setConfiguration(configuration); return true; } diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/load/LoadModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/load/LoadModule.java deleted file mode 100644 index 7c61c4f2..00000000 --- a/unity-ads/src/main/java/com/unity3d/services/ads/load/LoadModule.java +++ /dev/null @@ -1,265 +0,0 @@ -package com.unity3d.services.ads.load; - -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.text.TextUtils; - -import com.unity3d.ads.IUnityAdsLoadListener; -import com.unity3d.ads.UnityAdsLoadOptions; -import com.unity3d.services.core.configuration.Configuration; -import com.unity3d.services.core.configuration.IInitializationListener; -import com.unity3d.services.core.configuration.IInitializationNotificationCenter; -import com.unity3d.services.core.configuration.InitializationNotificationCenter; -import com.unity3d.services.core.device.Device; -import com.unity3d.services.core.log.DeviceLog; -import com.unity3d.services.core.misc.Utilities; -import com.unity3d.services.core.properties.SdkProperties; -import com.unity3d.services.core.request.SDKMetrics; -import com.unity3d.services.core.webview.WebViewApp; -import com.unity3d.services.core.webview.bridge.CallbackStatus; - -import org.json.JSONObject; - -import java.lang.reflect.Method; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class LoadModule implements IInitializationListener { - - private static LoadModule instance; - private static Configuration _configuration; - - public static LoadModule getInstance() { - if (instance == null) { - IInitializationNotificationCenter initializationNotificationCenter = InitializationNotificationCenter.getInstance(); - instance = new LoadModule(initializationNotificationCenter); - } - return instance; - } - - private class LoadEventState { - public String placementId; - public String listenerId; - public long time; - public IUnityAdsLoadListener listener; - public Runnable timeoutRunnable; - public UnityAdsLoadOptions loadOptions; - - LoadEventState(String placementId, String listenerId, IUnityAdsLoadListener listener, Runnable timeoutRunnable, long elapsedRealtime, UnityAdsLoadOptions loadOptions) { - this.placementId = placementId; - this.listenerId = listenerId; - this.listener = listener; - this.time = elapsedRealtime; - this.timeoutRunnable = timeoutRunnable; - this.loadOptions = loadOptions; - } - } - - private final LinkedList _loadEventBuffer; - private final LinkedHashMap _loadListeners; - private Method _loadCallback; - private Handler _handler; - private ExecutorService _executorService; - - public LoadModule(IInitializationNotificationCenter initializationNotificationCenter) { - _loadEventBuffer = new LinkedList<>(); - _loadListeners = new LinkedHashMap<>(); - _executorService = Executors.newSingleThreadExecutor(); - try { - _loadCallback = LoadModule.class.getMethod("loadCallback", CallbackStatus.class); - } catch (NoSuchMethodException e) { - _loadCallback = null; - } - _handler = new Handler(Looper.getMainLooper()); - if (_configuration == null) { - _configuration = new Configuration(); - } - - initializationNotificationCenter.addListener(this); - } - - public void load(final String placementId, final UnityAdsLoadOptions loadOptions, final IUnityAdsLoadListener listener) { - if (TextUtils.isEmpty(placementId)) { - Utilities.runOnUiThread(new Runnable() { - @Override - public void run() { - listener.onUnityAdsFailedToLoad(placementId); - } - }); - return; - } - - final LoadEventState loadEventState = createLoadEvent(placementId, listener, loadOptions); - - if (SdkProperties.getCurrentInitializationState() == SdkProperties.InitializationState.INITIALIZED_SUCCESSFULLY) { - _executorService.submit(new Runnable() { - @Override - public void run() { - LoadModule.this.runLoadRequest(loadEventState); - } - }); - } else if (SdkProperties.getCurrentInitializationState() == SdkProperties.InitializationState.INITIALIZED_FAILED) { - sendAdFailedToLoad(placementId, loadEventState.listenerId); - } else { - synchronized (_loadEventBuffer) { - _loadEventBuffer.add(loadEventState); - } - } - } - - private void runLoadRequest(LoadEventState loadEventState) { - try { - if (!load(loadEventState)) { - throw new Exception("Failed to send load request to WebView"); - } - } catch (Exception e) { - DeviceLog.error(e.getMessage()); - sendAdFailedToLoad(loadEventState.placementId, loadEventState.listenerId); - } - } - - public void sendAdLoaded(final String placementId, String listenerId) { - LoadEventState loadEventState; - synchronized (_loadListeners) { - loadEventState = _loadListeners.remove(listenerId); - } - - if (loadEventState == null) { - return; - } - - _handler.removeCallbacks(loadEventState.timeoutRunnable); - - final IUnityAdsLoadListener listener = loadEventState.listener; - if (listener == null) { - return; - } - - Utilities.runOnUiThread(new Runnable() { - @Override - public void run() { - listener.onUnityAdsAdLoaded(placementId); - } - }); - } - - public void sendAdFailedToLoad(final String placementId, String listenerId) { - LoadEventState loadEventState; - synchronized (_loadListeners) { - loadEventState =_loadListeners.remove(listenerId); - } - - if (loadEventState == null) { - return; - } - - _handler.removeCallbacks(loadEventState.timeoutRunnable); - - final IUnityAdsLoadListener listener = loadEventState.listener; - if (listener == null) { - return; - } - - Utilities.runOnUiThread(new Runnable() { - @Override - public void run() { - listener.onUnityAdsFailedToLoad(placementId); - } - }); - } - - private LoadEventState createLoadEvent(final String placementId, IUnityAdsLoadListener listener, UnityAdsLoadOptions loadOptions) { - final String listenerId = UUID.randomUUID().toString(); - - Runnable timeoutRunnable = new Runnable() { - @Override - public void run() { - LoadModule.this.sendAdFailedToLoad(placementId, listenerId); - } - }; - - LoadEventState loadEventState = new LoadEventState(placementId, listenerId, listener, timeoutRunnable, Device.getElapsedRealtime(), loadOptions); - - synchronized (_loadListeners) { - _loadListeners.put(listenerId, loadEventState); - } - - _handler.postDelayed(timeoutRunnable, _configuration.getNoFillTimeout()); - - return loadEventState; - } - - @Override - public void onSdkInitialized() { - final LoadEventState[] loadEventStates; - synchronized (_loadEventBuffer) { - loadEventStates = new LoadEventState[_loadEventBuffer.size()]; - _loadEventBuffer.toArray(loadEventStates); - _loadEventBuffer.clear(); - } - - _executorService.submit(new Runnable() { - @Override - public void run() { - for (LoadEventState loadEventState : loadEventStates) { - runLoadRequest(loadEventState); - } - } - }); - } - - @Override - public void onSdkInitializationFailed(String message, int code) { - final LoadEventState[] loadEventStates; - synchronized (_loadEventBuffer) { - loadEventStates = new LoadEventState[_loadEventBuffer.size()]; - _loadEventBuffer.toArray(loadEventStates); - _loadEventBuffer.clear(); - } - - for (LoadEventState loadEventState : loadEventStates) { - sendAdFailedToLoad(loadEventState.placementId, loadEventState.listenerId); - } - } - - private static ConditionVariable _waitLoadStatus; - private static volatile CallbackStatus _lastStatus = CallbackStatus.ERROR; - - private synchronized boolean load(LoadEventState loadEvent) throws Exception { - if (_loadCallback == null) { - throw new Exception("Callback for load request was not found"); - } - - JSONObject options = new JSONObject(); - options.put("listenerId", loadEvent.listenerId); - options.put("placementId", loadEvent.placementId); - options.put("time", loadEvent.time); - options.put("options", loadEvent.loadOptions.getData()); - - _lastStatus = CallbackStatus.ERROR; - _waitLoadStatus = new ConditionVariable(); - WebViewApp.getCurrentApp().invokeMethod("webview", "load", this._loadCallback, options); - boolean success = _waitLoadStatus.block(_configuration.getLoadTimeout()); - _waitLoadStatus = null; - if (!success) { - SDKMetrics.getInstance().sendEvent("native_load_callback_failed"); - } - return success && _lastStatus == CallbackStatus.OK; - } - - public static void loadCallback(CallbackStatus status) { - if (_waitLoadStatus != null) { - _lastStatus = status; - _waitLoadStatus.open(); - } - } - - public static void setConfiguration (Configuration configuration) { - _configuration = configuration; - } - -} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/AdModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/AdModule.java new file mode 100644 index 00000000..98eb51f9 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/AdModule.java @@ -0,0 +1,19 @@ +package com.unity3d.services.ads.operation; + +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.request.SDKMetricSender; +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; +import com.unity3d.services.core.webview.bridge.WebViewBridgeSharedObjectStore; + +public abstract class AdModule extends WebViewBridgeSharedObjectStore implements IAdModule { + protected ISDKMetricSender _sdkMetricSender; + + protected AdModule(ISDKMetricSender sdkMetricSender) { + super(); + _sdkMetricSender = sdkMetricSender; + } + + public ISDKMetricSender getMetricSender() { + return _sdkMetricSender; + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/AdOperation.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/AdOperation.java new file mode 100644 index 00000000..4dd8fcbb --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/AdOperation.java @@ -0,0 +1,29 @@ +package com.unity3d.services.ads.operation; + +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; + +import java.util.UUID; + +public abstract class AdOperation implements IAdOperation { + private static String invocationClassName = "webview"; + + private IWebViewBridgeInvocation _webViewBridgeInvocation; + private String _invocationMethodName; + + protected AdOperation(IWebViewBridgeInvocation webViewBridgeInvocation, String invocationMethodName) throws NullPointerException { + _invocationMethodName = invocationMethodName; + if (_invocationMethodName == null || _invocationMethodName == "") { + throw new IllegalArgumentException("invocationMethodName cannot be null"); + } + + _webViewBridgeInvocation = webViewBridgeInvocation; + if (_webViewBridgeInvocation == null) { + throw new IllegalArgumentException("webViewBridgeInvocation cannot be null"); + } + } + + @Override + public void invoke(final int timeout, final Object... invocationParameters) { + _webViewBridgeInvocation.invoke(invocationClassName, _invocationMethodName, timeout, invocationParameters); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdModule.java new file mode 100644 index 00000000..1c96b723 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdModule.java @@ -0,0 +1,11 @@ +package com.unity3d.services.ads.operation; + +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeSharedObjectStore; +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; + +public interface IAdModule extends IWebViewBridgeSharedObjectStore { + void executeAdOperation(final IWebViewBridgeInvoker webViewBridgeInvoker, final T2 state); + ISDKMetricSender getMetricSender(); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdOperation.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdOperation.java new file mode 100644 index 00000000..0a26858e --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/IAdOperation.java @@ -0,0 +1,7 @@ +package com.unity3d.services.ads.operation; + +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; + +public interface IAdOperation extends IWebViewSharedObject { + void invoke(final int timeout, final Object...invocationParameters); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadModule.java new file mode 100644 index 00000000..989a77f5 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadModule.java @@ -0,0 +1,9 @@ +package com.unity3d.services.ads.operation.load; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.IAdModule; + +public interface ILoadModule extends IAdModule { + void onUnityAdsAdLoaded(String operationId); + void onUnityAdsFailedToLoad(String operationId, UnityAds.UnityAdsLoadError error, String message); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadOperation.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadOperation.java new file mode 100644 index 00000000..aec89b39 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/ILoadOperation.java @@ -0,0 +1,8 @@ +package com.unity3d.services.ads.operation.load; + +import com.unity3d.ads.IUnityAdsLoadListener; +import com.unity3d.services.ads.operation.IAdOperation; + +public interface ILoadOperation extends IAdOperation, IUnityAdsLoadListener { + LoadOperationState getLoadOperationState(); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModule.java new file mode 100644 index 00000000..bc3f9ce4 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModule.java @@ -0,0 +1,118 @@ +package com.unity3d.services.ads.operation.load; + +import android.text.TextUtils; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.AdModule; +import com.unity3d.services.core.configuration.InitializationNotificationCenter; +import com.unity3d.services.core.device.Device; +import com.unity3d.services.core.misc.Utilities; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.request.SDKMetricSender; +import com.unity3d.services.core.webview.bridge.CallbackStatus; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocationCallback; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocation; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; + +public class LoadModule extends AdModule implements ILoadModule { + private static ILoadModule _instance; + + private static String errorMsgInternalCommunicationFailure = "[UnityAds] Internal communication failure"; + private static String errorMsgInternalCommunicationTimeout = "[UnityAds] Internal communication timeout"; + private static String errorMsgPlacementIdNull = "[UnityAds] Placement ID cannot be null"; + private static String errorMsgFailedToCreateLoadRequest = "[UnityAds] Failed to create load request"; + + public static ILoadModule getInstance() { + if (_instance == null) { + LoadModule loadModule = new LoadModule(new SDKMetricSender()); + LoadModuleDecoratorInitializationBuffer bufferedLoadModule = new LoadModuleDecoratorInitializationBuffer(loadModule, InitializationNotificationCenter.getInstance()); + LoadModuleDecoratorTimeout timedLoadModule = new LoadModuleDecoratorTimeout(bufferedLoadModule); + _instance = timedLoadModule; + } + return _instance; + } + + public LoadModule(ISDKMetricSender sdkMetricSender) { + super(sdkMetricSender); + } + + @Override + public void executeAdOperation(final IWebViewBridgeInvoker webViewBridgeInvoker, final LoadOperationState state) { + if (TextUtils.isEmpty(state.placementId)) { + sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INVALID_ARGUMENT, errorMsgPlacementIdNull); + return; + } + + LoadOperation loadOperation = new LoadOperation(state, new WebViewBridgeInvocation(webViewBridgeInvoker, new IWebViewBridgeInvocationCallback() { + @Override + public void onSuccess() { + + } + @Override + public void onFailure(String message, CallbackStatus callbackStatus) { + sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, errorMsgInternalCommunicationFailure); + + final String cbs = callbackStatus == null ? "invocationFailure" : callbackStatus.toString(); + _sdkMetricSender.SendSDKMetricEventWithTag(SDKMetricEvents.native_load_callback_error, new HashMap(){{ + put("cbs", cbs); + }}); + + remove(state.id); + } + @Override + public void onTimeout() { + sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, errorMsgInternalCommunicationTimeout); + getMetricSender().SendSDKMetricEvent(SDKMetricEvents.native_load_callback_timeout); + remove(state.id); + } + })); + + JSONObject invocationParameters = new JSONObject(); + try { + invocationParameters.put("listenerId", loadOperation.getId()); + invocationParameters.put("placementId", state.placementId); + invocationParameters.put("time", Device.getElapsedRealtime()); + invocationParameters.put("options", state.loadOptions.getData()); + } catch (JSONException e) { + sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, errorMsgFailedToCreateLoadRequest); + return; + } catch (NullPointerException e) { + sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, errorMsgFailedToCreateLoadRequest); + return; + } + + set(loadOperation); + loadOperation.invoke(state.configuration.getWebViewBridgeTimeout(), invocationParameters); + } + + public void onUnityAdsAdLoaded(String operationId) { + final ILoadOperation loadOperation = get(operationId); + if (loadOperation == null || loadOperation.getLoadOperationState() == null) return; + loadOperation.onUnityAdsAdLoaded(loadOperation.getLoadOperationState().placementId); + remove(operationId); + } + + @Override + public void onUnityAdsFailedToLoad(String operationId, UnityAds.UnityAdsLoadError error, String message) { + final ILoadOperation loadOperation = get(operationId); + if (loadOperation == null || loadOperation.getLoadOperationState() == null) return; + loadOperation.onUnityAdsFailedToLoad(loadOperation.getLoadOperationState().placementId, error, message); + remove(operationId); + } + + private void sendOnUnityAdsFailedToLoad(final LoadOperationState state, final UnityAds.UnityAdsLoadError error, final String message) { + if (state == null || state.listener == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { + state.listener.onUnityAdsFailedToLoad(state.placementId, error, message); + } + }); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecorator.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecorator.java new file mode 100644 index 00000000..9d7ac955 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecorator.java @@ -0,0 +1,48 @@ +package com.unity3d.services.ads.operation.load; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +public class LoadModuleDecorator implements ILoadModule { + private final ILoadModule _loadModule; + + public LoadModuleDecorator(ILoadModule loadModule) { + _loadModule = loadModule; + } + + @Override + public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, LoadOperationState state) { + _loadModule.executeAdOperation(webViewBridgeInvoker, state); + } + + @Override + public ISDKMetricSender getMetricSender() { + return _loadModule.getMetricSender(); + } + + @Override + public void onUnityAdsAdLoaded(String operationId) { + _loadModule.onUnityAdsAdLoaded(operationId); + } + + @Override + public void onUnityAdsFailedToLoad(String operationId, UnityAds.UnityAdsLoadError error, String message) { + _loadModule.onUnityAdsFailedToLoad(operationId, error, message); + } + + @Override + public ILoadOperation get(String id) { + return _loadModule.get(id); + } + + @Override + public void set(ILoadOperation sharedObject) { + _loadModule.set(sharedObject); + } + + @Override + public void remove(String id) { + _loadModule.remove(id); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorInitializationBuffer.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorInitializationBuffer.java new file mode 100644 index 00000000..07232c57 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorInitializationBuffer.java @@ -0,0 +1,68 @@ +package com.unity3d.services.ads.operation.load; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.core.configuration.IInitializationListener; +import com.unity3d.services.core.configuration.IInitializationNotificationCenter; +import com.unity3d.services.core.misc.Utilities; +import com.unity3d.services.core.properties.SdkProperties; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class LoadModuleDecoratorInitializationBuffer extends LoadModuleDecorator implements IInitializationListener { + private static String errorMsgInitializationFailed = "[UnityAds] SDK Initialization Failed"; + private static String errorMsgInitializationFailure = "[UnityAds] SDK Initialization Failure"; + + private ConcurrentHashMap _queuedLoadEvents; + + public LoadModuleDecoratorInitializationBuffer(ILoadModule loadModule, IInitializationNotificationCenter initializationNotificationCenter) { + super(loadModule); + initializationNotificationCenter.addListener(this); + _queuedLoadEvents = new ConcurrentHashMap<>(); + + } + + @Override + public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, LoadOperationState state) { + if (state == null) return; + + switch (SdkProperties.getCurrentInitializationState()) { + case INITIALIZED_SUCCESSFULLY: + super.executeAdOperation(webViewBridgeInvoker, state); + break; + case INITIALIZED_FAILED: + sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INITIALIZE_FAILED, errorMsgInitializationFailed); + break; + default: + _queuedLoadEvents.put(state, webViewBridgeInvoker); + break; + } + } + + @Override + public synchronized void onSdkInitialized() { + for (Map.Entry queuedLoadEvent : _queuedLoadEvents.entrySet()) { + super.executeAdOperation(queuedLoadEvent.getValue(), queuedLoadEvent.getKey()); + } + _queuedLoadEvents.clear(); + } + + @Override + public synchronized void onSdkInitializationFailed(String message, int code) { + for (LoadOperationState queuedLoadOperationState : _queuedLoadEvents.keySet()) { + sendOnUnityAdsFailedToLoad(queuedLoadOperationState, UnityAds.UnityAdsLoadError.INITIALIZE_FAILED, errorMsgInitializationFailure); + } + _queuedLoadEvents.clear(); + } + + private void sendOnUnityAdsFailedToLoad(final LoadOperationState state, final UnityAds.UnityAdsLoadError error, final String message) { + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { + state.listener.onUnityAdsFailedToLoad(state.placementId, error, message); + } + }); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorTimeout.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorTimeout.java new file mode 100644 index 00000000..b7c6bc01 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModuleDecoratorTimeout.java @@ -0,0 +1,66 @@ +package com.unity3d.services.ads.operation.load; + +import android.os.ConditionVariable; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class LoadModuleDecoratorTimeout extends LoadModuleDecorator { + private static String errorMsgTimeoutLoading = "[UnityAds] Timeout while loading "; + + private ExecutorService _executorService; + + public LoadModuleDecoratorTimeout(ILoadModule loadModule) { + super(loadModule); + _executorService = Executors.newCachedThreadPool(); + } + + @Override + public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, LoadOperationState state) { + startLoadTimeout(state); + super.executeAdOperation(webViewBridgeInvoker, state); + } + + private void startLoadTimeout(final LoadOperationState loadOperationState) { + _executorService.submit(new Runnable() { + @Override + public void run() { + if (!loadOperationState.timeoutCV.block(loadOperationState.configuration.getLoadTimeout())) { + onOperationTimeout(loadOperationState); + } + } + }); + } + + @Override + public void onUnityAdsAdLoaded(String operationId) { + releaseOperationTimeoutLock(operationId); + super.onUnityAdsAdLoaded(operationId); + } + + @Override + public void onUnityAdsFailedToLoad(String operationId, UnityAds.UnityAdsLoadError error, String message) { + releaseOperationTimeoutLock(operationId); + super.onUnityAdsFailedToLoad(operationId, error, message); + } + + private void releaseOperationTimeoutLock(String operationId) { + ILoadOperation loadOperation = get(operationId); + if (loadOperation == null) return; + LoadOperationState loadOperationState = loadOperation.getLoadOperationState(); + if (loadOperationState == null) return; + ConditionVariable timeoutCV = loadOperation.getLoadOperationState().timeoutCV; + if (timeoutCV == null) return; + loadOperation.getLoadOperationState().timeoutCV.open(); + } + + private void onOperationTimeout(final LoadOperationState state) { + remove(state.id); + state.listener.onUnityAdsFailedToLoad(state.placementId, UnityAds.UnityAdsLoadError.TIMEOUT, errorMsgTimeoutLoading + state.placementId); + getMetricSender().SendSDKMetricEvent(SDKMetricEvents.native_load_timeout_error); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperation.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperation.java new file mode 100644 index 00000000..400820b7 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperation.java @@ -0,0 +1,45 @@ +package com.unity3d.services.ads.operation.load; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.AdOperation; +import com.unity3d.services.core.misc.Utilities; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; + +public class LoadOperation extends AdOperation implements ILoadOperation { + private LoadOperationState _loadOperationState; + + public LoadOperation(LoadOperationState loadOperationState, IWebViewBridgeInvocation webViewBridgeInvocation) { + super(webViewBridgeInvocation, "load"); + _loadOperationState = loadOperationState; + } + + @Override + public LoadOperationState getLoadOperationState() { + return _loadOperationState; + } + + @Override + public void onUnityAdsAdLoaded(final String placementId) { + if (_loadOperationState == null || _loadOperationState.listener == null || placementId == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { _loadOperationState.listener.onUnityAdsAdLoaded(placementId); + } + }); + } + + @Override + public void onUnityAdsFailedToLoad(final String placementId, final UnityAds.UnityAdsLoadError error, final String message) { + if (_loadOperationState == null || _loadOperationState.listener == null || placementId == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { _loadOperationState.listener.onUnityAdsFailedToLoad(placementId, error, message); + } + }); + } + + @Override + public String getId() { + return _loadOperationState.id; + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperationState.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperationState.java new file mode 100644 index 00000000..10de3cdc --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadOperationState.java @@ -0,0 +1,33 @@ +package com.unity3d.services.ads.operation.load; + +import android.os.ConditionVariable; + +import com.unity3d.ads.IUnityAdsLoadListener; +import com.unity3d.ads.UnityAdsLoadOptions; +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; +import com.unity3d.services.core.configuration.Configuration; + +import java.util.UUID; + +public class LoadOperationState implements IWebViewSharedObject { + public String id; + public IUnityAdsLoadListener listener; + public UnityAdsLoadOptions loadOptions; + public String placementId; + public Configuration configuration; + public ConditionVariable timeoutCV; + + public LoadOperationState(String placementId, IUnityAdsLoadListener listener, UnityAdsLoadOptions loadOptions, Configuration configuration) { + this.listener = listener; + this.loadOptions = loadOptions; + this.placementId = placementId; + this.configuration = configuration; + this.timeoutCV = new ConditionVariable(); + id = UUID.randomUUID().toString(); + } + + @Override + public String getId() { + return id; + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowModule.java new file mode 100644 index 00000000..68a97f65 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowModule.java @@ -0,0 +1,11 @@ +package com.unity3d.services.ads.operation.show; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.IAdModule; + +public interface IShowModule extends IAdModule { + void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message); + void onUnityAdsShowStart(String id); + void onUnityAdsShowClick(String id); + void onUnityAdsShowComplete(String id, UnityAds.UnityAdsShowCompletionState state); +} \ No newline at end of file diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowOperation.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowOperation.java new file mode 100644 index 00000000..7f344c50 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/IShowOperation.java @@ -0,0 +1,8 @@ +package com.unity3d.services.ads.operation.show; + +import com.unity3d.ads.IUnityAdsShowListener; +import com.unity3d.services.ads.operation.IAdOperation; + +public interface IShowOperation extends IAdOperation, IUnityAdsShowListener { + ShowOperationState getShowOperationState(); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModule.java new file mode 100644 index 00000000..52068dd2 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModule.java @@ -0,0 +1,142 @@ +package com.unity3d.services.ads.operation.show; + +import android.graphics.Point; +import android.os.Build; +import android.text.TextUtils; +import android.view.Display; +import android.view.WindowManager; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.AdModule; +import com.unity3d.services.core.device.Device; +import com.unity3d.services.core.misc.Utilities; +import com.unity3d.services.core.properties.ClientProperties; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.request.SDKMetricSender; +import com.unity3d.services.core.webview.bridge.CallbackStatus; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocationCallback; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocation; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; + +public class ShowModule extends AdModule implements IShowModule { + private static IShowModule instance; + + public static String errorMsgPlacementIdNull = "[UnityAds] Placement ID cannot be null"; + + public static IShowModule getInstance() { + if (instance == null) { + instance = new ShowModuleDecoratorTimeout(new ShowModule(new SDKMetricSender())); + } + return instance; + } + + public ShowModule(ISDKMetricSender sdkMetricSender) { + super(sdkMetricSender); + } + + @Override + public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, final ShowOperationState state) { + if (TextUtils.isEmpty(state.placementId)) { + sendOnUnityAdsFailedToShow(state, errorMsgPlacementIdNull, UnityAds.UnityAdsShowError.INVALID_ARGUMENT); + return; + } + IShowOperation showOperation = new ShowOperation(state, new WebViewBridgeInvocation(webViewBridgeInvoker, new IWebViewBridgeInvocationCallback() { + @Override + public void onSuccess() { + } + @Override + public void onFailure(String message, CallbackStatus callbackStatus) { + sendOnUnityAdsFailedToShow(state, message, UnityAds.UnityAdsShowError.INTERNAL_ERROR); + + final String cbs = callbackStatus == null ? "invocationFailure" : callbackStatus.toString(); + _sdkMetricSender.SendSDKMetricEventWithTag(SDKMetricEvents.native_show_callback_error, new HashMap(){{ + put("cbs", cbs); + }}); + + remove(state.id); + } + @Override + public void onTimeout() { + sendOnUnityAdsFailedToShow(state, "[UnityAds] Show Invocation Timeout", UnityAds.UnityAdsShowError.INTERNAL_ERROR); + _sdkMetricSender.SendSDKMetricEvent(SDKMetricEvents.native_show_callback_timeout); + remove(state.id); + } + })); + + ClientProperties.setActivity(state.activity); + + Display defaultDisplay = ((WindowManager)state.activity.getSystemService(state.activity.WINDOW_SERVICE)).getDefaultDisplay(); + JSONObject options = new JSONObject(); + JSONObject display = new JSONObject(); + + try { + options.put("requestedOrientation", state.activity.getRequestedOrientation()); + display.put("rotation", defaultDisplay.getRotation()); + if (Build.VERSION.SDK_INT >= 13) { + Point displaySize = new Point(); + defaultDisplay.getSize(displaySize); + display.put("width", displaySize.x); + display.put("height", displaySize.y); + } else { + display.put("width", defaultDisplay.getWidth()); + display.put("height", defaultDisplay.getHeight()); + } + options.put("display", display); + options.put("options", state.showOptions.getData()); + options.put("listenerId", showOperation.getId()); + options.put("placementId", state.placementId); + options.put("time", Device.getElapsedRealtime()); + } catch (JSONException e) { + sendOnUnityAdsFailedToShow(state, "[UnityAds] Error creating show options", UnityAds.UnityAdsShowError.INTERNAL_ERROR); + return; + } catch (NullPointerException e) { + sendOnUnityAdsFailedToShow(state, "[UnityAds] Error creating show options", UnityAds.UnityAdsShowError.INTERNAL_ERROR); + return; + } + + set(showOperation); + showOperation.invoke(state.configuration.getWebViewBridgeTimeout(), options); + } + + public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message) { + final IShowOperation showOperation = get(id); + if (showOperation == null || showOperation.getShowOperationState() == null) return; + showOperation.onUnityAdsShowFailure(showOperation.getShowOperationState().placementId, error, message); + remove(id); + } + + public void onUnityAdsShowStart(String id) { + final IShowOperation showOperation = get(id); + if (showOperation == null || showOperation.getShowOperationState() == null) return; + showOperation.onUnityAdsShowStart(showOperation.getShowOperationState().placementId); + } + + public void onUnityAdsShowClick(String id) { + final IShowOperation showOperation = get(id); + if (showOperation == null || showOperation.getShowOperationState() == null) return; + showOperation.onUnityAdsShowClick(showOperation.getShowOperationState().placementId); + } + + public void onUnityAdsShowComplete(String id, UnityAds.UnityAdsShowCompletionState state) { + final IShowOperation showOperation = get(id); + if (showOperation == null || showOperation.getShowOperationState() == null) return; + showOperation.onUnityAdsShowComplete(showOperation.getShowOperationState().placementId, state); + remove(id); + } + + private void sendOnUnityAdsFailedToShow(final ShowOperationState showOperationState, final String errorMessage, final UnityAds.UnityAdsShowError errorCode) { + if (showOperationState == null || showOperationState.listener == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { + showOperationState.listener.onUnityAdsShowFailure(showOperationState.placementId, errorCode, errorMessage); + } + }); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecorator.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecorator.java new file mode 100644 index 00000000..80ffa116 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecorator.java @@ -0,0 +1,58 @@ +package com.unity3d.services.ads.operation.show; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +public class ShowModuleDecorator implements IShowModule { + private final IShowModule _showModule; + + public ShowModuleDecorator(IShowModule showModule) { + _showModule = showModule; + } + + @Override + public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, ShowOperationState state) { + _showModule.executeAdOperation(webViewBridgeInvoker, state); + } + + @Override + public ISDKMetricSender getMetricSender() { + return _showModule.getMetricSender(); + } + + @Override + public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message) { + _showModule.onUnityAdsShowFailure(id, error, message); + } + + @Override + public void onUnityAdsShowStart(String id) { + _showModule.onUnityAdsShowStart(id); + } + + @Override + public void onUnityAdsShowClick(String id) { + _showModule.onUnityAdsShowClick(id); + } + + @Override + public void onUnityAdsShowComplete(String id, UnityAds.UnityAdsShowCompletionState state) { + _showModule.onUnityAdsShowComplete(id, state); + } + + @Override + public IShowOperation get(String id) { + return _showModule.get(id); + } + + @Override + public void set(IShowOperation sharedObject) { + _showModule.set(sharedObject); + } + + @Override + public void remove(String id) { + _showModule.remove(id); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecoratorTimeout.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecoratorTimeout.java new file mode 100644 index 00000000..0a57dd65 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowModuleDecoratorTimeout.java @@ -0,0 +1,66 @@ +package com.unity3d.services.ads.operation.show; + +import android.os.ConditionVariable; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.core.request.SDKMetricEvents; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ShowModuleDecoratorTimeout extends ShowModuleDecorator { + private static String errorMsgTimeout = "[UnityAds] Timeout while trying to show "; + + private ExecutorService _executorService; + + public ShowModuleDecoratorTimeout(IShowModule showModule) { + super(showModule); + _executorService = Executors.newSingleThreadExecutor(); + } + + @Override + public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, ShowOperationState state) { + startShowTimeout(state); + super.executeAdOperation(webViewBridgeInvoker, state); + } + + private void startShowTimeout(final ShowOperationState showOperationState) { + _executorService.submit(new Runnable() { + @Override + public void run() { + if (!showOperationState.timeoutCV.block(showOperationState.configuration.getShowTimeout())) { + onOperationTimeout(showOperationState, UnityAds.UnityAdsShowError.INTERNAL_ERROR, errorMsgTimeout + showOperationState.placementId); + } + } + }); + } + + @Override + public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message) { + releaseOperationTimeoutLock(id); + super.onUnityAdsShowFailure(id, error, message); + } + + @Override + public void onUnityAdsShowStart(String id) { + releaseOperationTimeoutLock(id); + super.onUnityAdsShowStart(id); + } + + private void releaseOperationTimeoutLock(String operationId) { + IShowOperation showOperation = get(operationId); + if (showOperation == null) return; + ShowOperationState showOperationState = showOperation.getShowOperationState(); + if (showOperationState == null) return; + ConditionVariable timeoutCV = showOperation.getShowOperationState().timeoutCV; + if (timeoutCV == null) return; + showOperation.getShowOperationState().timeoutCV.open(); + } + + private void onOperationTimeout(final ShowOperationState state, UnityAds.UnityAdsShowError error, String message) { + remove(state.id); + state.listener.onUnityAdsShowFailure(state.placementId, error, message); + getMetricSender().SendSDKMetricEvent(SDKMetricEvents.native_show_timeout_error); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperation.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperation.java new file mode 100644 index 00000000..4081383a --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperation.java @@ -0,0 +1,65 @@ +package com.unity3d.services.ads.operation.show; + +import com.unity3d.ads.UnityAds; +import com.unity3d.services.ads.operation.AdOperation; +import com.unity3d.services.core.misc.Utilities; +import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; + +public class ShowOperation extends AdOperation implements IShowOperation { + private ShowOperationState showOperationState; + + public ShowOperation(ShowOperationState showOperationState, IWebViewBridgeInvocation webViewBridgeInvocation) { + super(webViewBridgeInvocation, "show"); + this.showOperationState = showOperationState; + } + + @Override + public ShowOperationState getShowOperationState() { + return showOperationState; + } + + + @Override + public void onUnityAdsShowFailure(final String placementId, final UnityAds.UnityAdsShowError error, final String message) { + if (showOperationState == null || showOperationState.listener == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { showOperationState.listener.onUnityAdsShowFailure(placementId, error, message); + } + }); + } + + @Override + public void onUnityAdsShowStart(final String placementId) { + if (showOperationState == null || showOperationState.listener == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { showOperationState.listener.onUnityAdsShowStart(placementId); + } + }); + } + + @Override + public void onUnityAdsShowClick(final String placementId) { + if (showOperationState == null || showOperationState.listener == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { showOperationState.listener.onUnityAdsShowClick(placementId); + } + }); + } + + @Override + public void onUnityAdsShowComplete(final String placementId, final UnityAds.UnityAdsShowCompletionState state) { + if (showOperationState == null || showOperationState.listener == null) return; + Utilities.runOnUiThread(new Runnable() { + @Override + public void run() { showOperationState.listener.onUnityAdsShowComplete(placementId, state); } + }); + } + + @Override + public String getId() { + return showOperationState.id; + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperationState.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperationState.java new file mode 100644 index 00000000..69a3a649 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/show/ShowOperationState.java @@ -0,0 +1,36 @@ +package com.unity3d.services.ads.operation.show; + +import android.app.Activity; +import android.os.ConditionVariable; + +import com.unity3d.ads.IUnityAdsShowListener; +import com.unity3d.ads.UnityAdsShowOptions; +import com.unity3d.services.core.configuration.Configuration; +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; + +import java.util.UUID; + +public class ShowOperationState implements IWebViewSharedObject { + public String id; + public Activity activity; + public IUnityAdsShowListener listener; + public String placementId; + public UnityAdsShowOptions showOptions; + public Configuration configuration; + public ConditionVariable timeoutCV; + + public ShowOperationState(String placementId, IUnityAdsShowListener listener, Activity activity, UnityAdsShowOptions showOptions, Configuration configuration) { + this.listener = listener; + this.placementId = placementId; + this.activity = activity; + this.showOptions = showOptions; + this.configuration = configuration; + this.timeoutCV = new ConditionVariable(); + id = UUID.randomUUID().toString(); + } + + @Override + public String getId() { + return id; + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/configuration/Configuration.java b/unity-ads/src/main/java/com/unity3d/services/core/configuration/Configuration.java index d17b84a9..e1966faf 100644 --- a/unity-ads/src/main/java/com/unity3d/services/core/configuration/Configuration.java +++ b/unity-ads/src/main/java/com/unity3d/services/core/configuration/Configuration.java @@ -32,7 +32,7 @@ public class Configuration { private long _networkErrorTimeout; private int _showTimeout; private int _loadTimeout; - private int _noFillTimeout; + private int _webViewBridgeTimeout; private String _metricsUrl; private double _metricSampleRate; private long _webViewAppCreateTimeout; @@ -116,7 +116,7 @@ public Class[] getWebAppApiClassList () { public int getLoadTimeout() { return _loadTimeout; } - public int getNoFillTimeout() { return _noFillTimeout; } + public int getWebViewBridgeTimeout() { return _webViewBridgeTimeout; } public String getMetricsUrl() { return _metricsUrl; } @@ -209,9 +209,9 @@ private void setOptionalFields(JSONObject configData) { _maximumConnectedEvents = configData.optInt("mce", 500); _networkErrorTimeout = configData.optLong("net", 60000L); _sdkVersion = configData.optString("sdkv", ""); - _showTimeout = configData.optInt("sto", 5000); - _loadTimeout = configData.optInt("lto", 5000); - _noFillTimeout = configData.optInt("nft", 30000); + _showTimeout = configData.optInt("sto", 10000); + _loadTimeout = configData.optInt("lto", 30000); + _webViewBridgeTimeout = configData.optInt("wto", 5000); _metricsUrl = configData.optString("murl", ""); _metricSampleRate = configData.optDouble("msr", 100d); _webViewAppCreateTimeout = configData.optLong("wct", 60000L); diff --git a/unity-ads/src/main/java/com/unity3d/services/core/request/ISDKMetricSender.java b/unity-ads/src/main/java/com/unity3d/services/core/request/ISDKMetricSender.java new file mode 100644 index 00000000..a5590483 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/request/ISDKMetricSender.java @@ -0,0 +1,8 @@ +package com.unity3d.services.core.request; + +import java.util.HashMap; + +public interface ISDKMetricSender { + void SendSDKMetricEvent(SDKMetricEvents metricEvent); + void SendSDKMetricEventWithTag(SDKMetricEvents metricEvent, HashMap tags); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricEvents.java b/unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricEvents.java new file mode 100644 index 00000000..eef9c9f0 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricEvents.java @@ -0,0 +1,10 @@ +package com.unity3d.services.core.request; + +public enum SDKMetricEvents { + native_load_callback_error, + native_load_callback_timeout, + native_show_callback_error, + native_show_callback_timeout, + native_load_timeout_error, + native_show_timeout_error +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricSender.java b/unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricSender.java new file mode 100644 index 00000000..c394c27f --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/request/SDKMetricSender.java @@ -0,0 +1,19 @@ +package com.unity3d.services.core.request; + +import java.util.HashMap; + +public class SDKMetricSender implements ISDKMetricSender { + public void SendSDKMetricEvent(SDKMetricEvents metricEvent) { + if (metricEvent == null) return; + ISDKMetrics sdkMetric = SDKMetrics.getInstance(); + if (sdkMetric == null) return; + sdkMetric.sendEvent(metricEvent.toString()); + } + + public void SendSDKMetricEventWithTag(SDKMetricEvents metricEvent, HashMap tags) { + if (metricEvent == null) return; + ISDKMetrics sdkMetric = SDKMetrics.getInstance(); + if (sdkMetric == null) return; + sdkMetric.sendEventWithTags(metricEvent.toString(), tags); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/WebViewApp.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/WebViewApp.java index e9e0e0c2..4543f6bd 100644 --- a/unity-ads/src/main/java/com/unity3d/services/core/webview/WebViewApp.java +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/WebViewApp.java @@ -3,7 +3,6 @@ import android.os.Build; import android.os.ConditionVariable; import android.os.Looper; -import android.view.ViewGroup; import android.webkit.RenderProcessGoneDetail; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; @@ -11,6 +10,7 @@ import android.webkit.WebViewClient; import com.unity3d.services.ads.api.AdUnit; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; import com.unity3d.services.core.configuration.Configuration; import com.unity3d.services.core.configuration.InitializeThread; import com.unity3d.services.core.log.DeviceLog; @@ -36,7 +36,7 @@ import java.util.HashMap; import java.util.concurrent.atomic.AtomicReference; -public class WebViewApp { +public class WebViewApp implements IWebViewBridgeInvoker { private static WebViewApp _currentApp; private static ConditionVariable _conditionVariable; @@ -156,6 +156,7 @@ public boolean sendEvent (Enum eventCategory, Enum eventId, Object... params) { return true; } + @Override public boolean invokeMethod(String className, String methodName, Method callback, Object... params) { if(!isWebAppLoaded()) { DeviceLog.debug("invokeMethod ignored because web app is not loaded"); diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeInvoker.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeInvoker.java new file mode 100644 index 00000000..3cc7c21c --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeInvoker.java @@ -0,0 +1,7 @@ +package com.unity3d.services.core.webview.bridge; + +import java.lang.reflect.Method; + +public interface IWebViewBridgeInvoker { + boolean invokeMethod(String className, String methodName, Method callbackMethod, Object...options); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeSharedObjectStore.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeSharedObjectStore.java new file mode 100644 index 00000000..5317d66e --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewBridgeSharedObjectStore.java @@ -0,0 +1,7 @@ +package com.unity3d.services.core.webview.bridge; + +public interface IWebViewBridgeSharedObjectStore { + T get(String id); + void set(T sharedObject); + void remove(String id); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewSharedObject.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewSharedObject.java new file mode 100644 index 00000000..2b66adda --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/IWebViewSharedObject.java @@ -0,0 +1,5 @@ +package com.unity3d.services.core.webview.bridge; + +public interface IWebViewSharedObject { + String getId(); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeInvoker.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeInvoker.java new file mode 100644 index 00000000..7503b5d2 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeInvoker.java @@ -0,0 +1,12 @@ +package com.unity3d.services.core.webview.bridge; + +import com.unity3d.services.core.webview.WebViewApp; + +import java.lang.reflect.Method; + +public class WebViewBridgeInvoker implements IWebViewBridgeInvoker { + @Override + public boolean invokeMethod(String className, String methodName, Method callbackMethod, Object... options) { + return WebViewApp.getCurrentApp().invokeMethod(className, methodName, callbackMethod, options); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeSharedObjectStore.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeSharedObjectStore.java new file mode 100644 index 00000000..ae295c48 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/WebViewBridgeSharedObjectStore.java @@ -0,0 +1,26 @@ +package com.unity3d.services.core.webview.bridge; + +import java.util.concurrent.ConcurrentHashMap; + +public abstract class WebViewBridgeSharedObjectStore implements IWebViewBridgeSharedObjectStore { + private ConcurrentHashMap _sharedObjects = new ConcurrentHashMap<>(); + + public T get(String id) { + if (id == null) return null; + return _sharedObjects.get(id); + } + + public void set(T sharedObject) { + if (sharedObject == null) return; + _sharedObjects.put(sharedObject.getId(), sharedObject); + } + + public void remove(T sharedObject) { + if (sharedObject == null) return; + remove(sharedObject.getId()); + } + + public void remove(String id) { + _sharedObjects.remove(id); + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocation.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocation.java new file mode 100644 index 00000000..4f06aaf0 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocation.java @@ -0,0 +1,5 @@ +package com.unity3d.services.core.webview.bridge.invocation; + +public interface IWebViewBridgeInvocation { + void invoke(String className, String methodName, int timeoutLengthInMilliSeconds, Object...invocationParameters); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocationCallback.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocationCallback.java new file mode 100644 index 00000000..01567d9b --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/IWebViewBridgeInvocationCallback.java @@ -0,0 +1,9 @@ +package com.unity3d.services.core.webview.bridge.invocation; + +import com.unity3d.services.core.webview.bridge.CallbackStatus; + +public interface IWebViewBridgeInvocationCallback { + void onSuccess(); + void onFailure(String message, CallbackStatus callbackStatus); + void onTimeout(); +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocation.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocation.java new file mode 100644 index 00000000..b68f5ff5 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocation.java @@ -0,0 +1,76 @@ +package com.unity3d.services.core.webview.bridge.invocation; + +import android.os.ConditionVariable; + +import com.unity3d.services.core.webview.bridge.CallbackStatus; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; + +import java.lang.reflect.Method; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class WebViewBridgeInvocation implements IWebViewBridgeInvocation { + private static ConditionVariable responseTimeout; + private static IWebViewBridgeInvocationCallback invocationCallback; + + private IWebViewBridgeInvoker _webViewBridgeInvoker; + private Method webViewBridgeCallbackMethod; + + private ExecutorService executorService; + + public WebViewBridgeInvocation(IWebViewBridgeInvoker webViewBridgeInvoker, IWebViewBridgeInvocationCallback invocationCallback) { + responseTimeout = new ConditionVariable(); + this.invocationCallback = invocationCallback; + + if (webViewBridgeInvoker == null) { + throw new IllegalArgumentException("webViewBridgeInvoker cannot be null"); + } + + _webViewBridgeInvoker = webViewBridgeInvoker; + executorService = Executors.newSingleThreadExecutor(); + + try { + webViewBridgeCallbackMethod = WebViewBridgeInvocation.class.getMethod("onInvocationComplete", CallbackStatus.class); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("loadCallback cannot be null"); + } + } + + @Override + public synchronized void invoke(final String className, final String methodName, final int timeoutLengthInMilliSeconds, final Object...invocationParameters) { + executorService.submit(new Runnable() { + @Override + public void run() { + if (!_webViewBridgeInvoker.invokeMethod(className, methodName, webViewBridgeCallbackMethod, invocationParameters)) { + if (invocationCallback != null) { + invocationCallback.onFailure("WebViewBridgeInvocation:execute: invokeMethod failure", null); + } + return; + } + + if (!responseTimeout.block(timeoutLengthInMilliSeconds)) { + if (invocationCallback != null) { + invocationCallback.onTimeout(); + } + } + } + }); + } + + public static synchronized void onInvocationComplete(CallbackStatus status) { + if (responseTimeout != null) { + responseTimeout.open(); + } + + if (invocationCallback == null) return; + + switch (status) { + case OK: + invocationCallback.onSuccess(); + break; + default: + invocationCallback.onFailure("WebViewBridgeInvocation:OnInvocationComplete: CallbackStatus.Error", status); + break; + } + } +} \ No newline at end of file