From 618ce50538db79d18242d511a373ce32c524250c Mon Sep 17 00:00:00 2001 From: lmoreault Date: Wed, 26 May 2021 15:18:08 -0400 Subject: [PATCH] Release 3.7.2 --- app/build.gradle | 5 +- build.gradle | 26 +-- gradle.properties | 5 +- gradle/wrapper/gradle-wrapper.jar | Bin gradle/wrapper/gradle-wrapper.properties | 4 +- unity-ads/artifactory.gradle | 21 ++ unity-ads/build.gradle | 181 +++++------------- unity-ads/jacoco.gradle | 26 +++ unity-ads/mavenPomBintrayUpload.gradle | 42 ---- unity-ads/publishing.gradle | 46 +++++ .../ads/test/InstrumentationTestSuite.java | 2 + .../ads/operation/LoadModuleTests.java | 2 +- .../ShowModuleDecoratorTimeoutTests.java | 85 ++++++++ .../ads/operation/ShowModuleTests.java | 4 +- .../WebViewBridgeInvocationRunnableTests.java | 115 +++++++++++ .../WebViewBridgeInvocationTests.java | 100 +--------- .../com/unity3d/services/ads/api/Show.java | 7 + .../services/ads/operation/AdModule.java | 6 +- .../services/ads/operation/AdOperation.java | 2 - .../ads/operation/OperationState.java | 29 +++ .../ads/operation/load/LoadModule.java | 17 +- ...adModuleDecoratorInitializationBuffer.java | 1 - .../operation/load/LoadOperationState.java | 22 +-- .../ads/operation/show/IShowModule.java | 1 + .../ads/operation/show/ShowModule.java | 23 ++- .../operation/show/ShowModuleDecorator.java | 3 + .../show/ShowModuleDecoratorTimeout.java | 6 + .../operation/show/ShowOperationState.java | 21 +- .../services/ads/video/VideoPlayerView.java | 12 +- .../invocation/WebViewBridgeInvocation.java | 61 +----- .../WebViewBridgeInvocationRunnable.java | 69 +++++++ ...ridgeInvocationSingleThreadedExecutor.java | 24 +++ 32 files changed, 564 insertions(+), 404 deletions(-) mode change 100644 => 100755 gradle/wrapper/gradle-wrapper.jar create mode 100644 unity-ads/artifactory.gradle create mode 100644 unity-ads/jacoco.gradle delete mode 100644 unity-ads/mavenPomBintrayUpload.gradle create mode 100644 unity-ads/publishing.gradle create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleDecoratorTimeoutTests.java create mode 100644 unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnableTests.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/ads/operation/OperationState.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnable.java create mode 100644 unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationSingleThreadedExecutor.java diff --git a/app/build.gradle b/app/build.gradle index d281e4e7..7558337e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,14 +2,13 @@ apply plugin: 'com.android.application' android { compileSdkVersion 30 - buildToolsVersion '28.0.2' defaultConfig { applicationId "com.unity3d.ads.example" minSdkVersion 19 targetSdkVersion 30 - versionCode = 3710 - versionName = "3.7.1" + versionCode = 3720 + versionName = "3.7.2" } flavorDimensions "arEnabled" diff --git a/build.gradle b/build.gradle index 533071f4..06f0c209 100644 --- a/build.gradle +++ b/build.gradle @@ -1,23 +1,23 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { +allprojects { repositories { + google() jcenter() + } +} + +buildscript { + repositories { + jcenter() + gradlePluginPortal() google() } 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' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' + classpath 'com.android.tools.build:gradle:4.1.3' classpath 'org.jacoco:org.jacoco.core:0.8.1' + classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.20.0' + classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' } } -allprojects { - repositories { - google() - jcenter() - } -} +apply from: 'nexusPublishing.gradle' \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ccd5dda1..636a0d37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,7 @@ # 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 -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true + +signing.keyId=34500891 +signing.secretKeyRingFile=../gpg-private-key.gpg \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar old mode 100644 new mode 100755 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 348ebbb5..36ca9b1b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Aug 09 12:15:12 EEST 2018 +#Thu Apr 08 20:52:07 EDT 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/unity-ads/artifactory.gradle b/unity-ads/artifactory.gradle new file mode 100644 index 00000000..ad24d271 --- /dev/null +++ b/unity-ads/artifactory.gradle @@ -0,0 +1,21 @@ +apply plugin: 'com.jfrog.artifactory' + +Properties properties = new Properties() +if (project.rootProject.file('local.properties').exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} + +artifactory { + contextUrl = 'https://unity3ddist.jfrog.io/artifactory' + publish { + repository { + repoKey = properties.getProperty("artifactory.repokey") + username = properties.getProperty("artifactory.username") + password = properties.getProperty("artifactory.apikey") + } + defaults { + // Tell the Artifactory Plugin which artifacts should be published to Artifactory. + publications('release') + } + } +} \ No newline at end of file diff --git a/unity-ads/build.gradle b/unity-ads/build.gradle index e6f7a957..cdd09ad1 100644 --- a/unity-ads/build.gradle +++ b/unity-ads/build.gradle @@ -1,62 +1,51 @@ -apply plugin: 'com.android.library' -apply plugin: 'com.jfrog.bintray' -apply plugin: 'com.github.dcendents.android-maven' -apply plugin: 'jacoco' - -ext { - bintrayRepo = "UnityAds" - bintrayName = "unityads" - - publishedGroupId = 'com.unity3d.ads' - libraryName = 'unity-ads' - artifact = 'unity-ads' - archivesBaseName = 'unity-ads' +import com.android.ddmlib.DdmPreferences - libraryDescription = 'Monetize your entire player base and reach new audiences with video ads.' - - siteUrl = 'https://github.com/Unity-Technologies/unity-ads-android' - gitUrl = 'https://github.com/Unity-Technologies/unity-ads-android.git' - - libraryVersion = '3.7.1' +apply plugin: 'com.android.library' - developerId = 'sbankhead' - developerName = 'Steven Bankhead' - developerEmail = 'sbankhead@unity3d.com' +Properties properties = new Properties() +if (project.rootProject.file('local.properties').exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} - licenseName = 'Unity Ads License' - licenseUrl = 'https://unity3d.com/legal/monetization-services-terms-of-service' - allLicenses = ["Unity Ads License"] +dependencies { + testImplementation 'junit:junit:4.12' + 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' } -version = libraryVersion +ext { + GROUP_ID = "com.unity3d.ads" + ARTIFACT_ID = "unity-ads" + VERSION_ID = "3.7.2" + VERSION_CODE = 3720 + SIGN_AAR = properties.getProperty("SIGN_AAR") ?: false +} android { compileSdkVersion 30 - buildToolsVersion '28.0.2' - com.android.ddmlib.DdmPreferences.setLogLevel("verbose") - com.android.ddmlib.DdmPreferences.setTimeOut(10 * 60000) + DdmPreferences.setLogLevel("verbose") + DdmPreferences.setTimeOut(10 * 60000) defaultPublishConfig "release" + lintOptions { + abortOnError false + } + defaultConfig { minSdkVersion 19 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 - mapping between normal SDK and China SDK. - - Example : version number 2350 corresponds to China version of 2300. - - All SDK with version numbers with last two digits >= 50 will be treated - as China SDK for filtering in the backend. - */ - versionCode = 3710 - versionName = "3.7.1" + versionCode = VERSION_CODE + versionName = VERSION_ID setProperty("archivesBaseName", "unity-ads") buildConfigField('String', 'WEBVIEW_BRANCH', getPropertyStringWithDefaultValue('WEBVIEW_BRANCH', '"' + versionName + '"')) + buildConfigField('int', 'VERSION_CODE', "$versionCode") + buildConfigField('String', 'VERSION_NAME', "\"$versionName\"") testBuildType "debug" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' @@ -80,59 +69,43 @@ android { consumerProguardFiles 'proguard-rules.pro' } } - - libraryVariants.all { variant -> - variant.outputs.all { output -> - if (outputFile != null && outputFileName.endsWith('.aar')) { - outputFileName = "${archivesBaseName}-${variant.buildType.name}.aar" - } - } - } - - testVariants.all { variant -> - variant.outputs.all { output -> - outputFileName = "../../androidTest.apk" - } - } -} - -dependencies { - testImplementation 'junit:junit:4.12' - 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) { +task javadoc(type: Javadoc) { description "Generates Javadoc for Release" source = android.sourceSets.main.java.srcDirs ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" + ext.generatedClass = "build/generated/source/buildConfig/release/" doFirst { - classpath = project.files(android.getBootClasspath().join(File.pathSeparator)) + files(ext.androidJar) + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += files(ext.androidJar) + classpath += fileTree(dir: 'libs', include: ['*.jar']) + classpath += files(ext.generatedClass) } 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/ARUtils.java' + exclude 'com/unity3d/services/ar/configuration/ARModuleConfiguration.java' exclude 'com/unity3d/services/ar/view/ARView.java' exclude 'com/unity3d/services/ar/view/ARViewHandler.java' + exclude 'com/unity3d/services/ar/view/BackgroundRenderer.java' + exclude 'com/unity3d/services/ar/view/DisplayRotationHelper.java' destinationDir = file("../javadoc/") } task androidJavadocsJar(type: Jar, dependsOn: javadoc) { - classifier = "javadoc" - baseName = "${archivesBaseName}" + archiveClassifier.set("javadoc") + archiveBaseName.set("${archivesBaseName}") from javadoc.destinationDir } task androidSourcesJar(type: Jar) { - classifier = "sources" - baseName = "${archivesBaseName}" + archiveClassifier.set("sources") + archiveBaseName.set("${archivesBaseName}") from android.sourceSets.main.java.srcDirs } @@ -141,66 +114,6 @@ artifacts { archives androidSourcesJar } -jacoco { - toolVersion = '0.8.1' -} - -tasks.withType(Test) { - jacoco.includeNoLocationClasses = true -} - -task jacocoTestReport(type: JacocoReport) { - reports { - xml.enabled = true - html.enabled = true - } - - def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*'] - def javaClasses = fileTree(dir: "$project.buildDir/intermediates/classes/debug", excludes: fileFilter) - def javaSrc = "$project.projectDir/src/main/java" - - sourceDirectories = files([javaSrc]) - classDirectories = files([javaClasses]) - executionData = fileTree(dir: project.buildDir, includes: [ - 'outputs/code-coverage/connected/*coverage.ec' - ]) -} - -// Bintray -task localProperties { - if (!file("$rootDir/local.properties").exists()) { - file("$rootDir/local.properties").withWriterAppend { w -> "" } - } -} - -bintray { - Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - - user = properties.getProperty("bintray.user") - key = properties.getProperty("bintray.apikey") - - configurations = ['archives'] - pkg { - repo = bintrayRepo - name = bintrayName - desc = libraryDescription - websiteUrl = siteUrl - vcsUrl = gitUrl - licenses = allLicenses - publish = true - publicDownloadNumbers = false - version { - desc = libraryDescription - gpg { - sign = true //Determines whether to GPG sign the files. The default is false - passphrase = properties.getProperty("bintray.gpg.password") - //Optional. The passphrase for GPG signing' - } - } - } -} - def getPropertyStringWithDefaultValue(String key, String defaultValue) { def value = project.getProperties().get(key) // Ensure that string is quoted @@ -211,6 +124,6 @@ def getPropertyStringWithDefaultValue(String key, String defaultValue) { return value != null ? value : defaultValue } -if (project.rootProject.file('local.properties').exists()) { - apply from: 'mavenPomBintrayUpload.gradle' -} \ No newline at end of file +apply from: 'publishing.gradle' +apply from: 'artifactory.gradle' +apply from: 'jacoco.gradle' \ No newline at end of file diff --git a/unity-ads/jacoco.gradle b/unity-ads/jacoco.gradle new file mode 100644 index 00000000..83126d87 --- /dev/null +++ b/unity-ads/jacoco.gradle @@ -0,0 +1,26 @@ +apply plugin: 'jacoco' + +jacoco { + toolVersion = '0.8.1' +} + +tasks.withType(Test) { + jacoco.includeNoLocationClasses = true +} + +task jacocoTestReport(type: JacocoReport) { + reports { + xml.enabled = true + html.enabled = true + } + + def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*'] + def javaClasses = fileTree(dir: "$project.buildDir/intermediates/classes/debug", excludes: fileFilter) + def javaSrc = "$project.projectDir/src/main/java" + + sourceDirectories.setFrom(files([javaSrc])) + classDirectories.setFrom(files([javaClasses])) + executionData.setFrom(fileTree(dir: project.buildDir, includes: [ + 'outputs/code-coverage/connected/*coverage.ec' + ])) +} \ No newline at end of file diff --git a/unity-ads/mavenPomBintrayUpload.gradle b/unity-ads/mavenPomBintrayUpload.gradle deleted file mode 100644 index cdb363c6..00000000 --- a/unity-ads/mavenPomBintrayUpload.gradle +++ /dev/null @@ -1,42 +0,0 @@ -apply plugin: 'com.github.dcendents.android-maven' - -group = publishedGroupId // Maven Group ID for the artifact - -install { - repositories.mavenInstaller { - // This generates POM.xml with proper parameters - pom { - project { - packaging 'aar' - groupId publishedGroupId - artifactId artifact - - // Add your description here - name libraryName - description libraryDescription - url siteUrl - - // Set your license - licenses { - license { - name licenseName - url licenseUrl - } - } - developers { - developer { - id developerId - name developerName - email developerEmail - } - } - scm { - connection gitUrl - developerConnection gitUrl - url siteUrl - - } - } - } - } -} \ No newline at end of file diff --git a/unity-ads/publishing.gradle b/unity-ads/publishing.gradle new file mode 100644 index 00000000..b7a8859b --- /dev/null +++ b/unity-ads/publishing.gradle @@ -0,0 +1,46 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' + +publishing { + publications { + release(MavenPublication) { + groupId GROUP_ID + artifactId ARTIFACT_ID + version VERSION_ID + + artifact("$buildDir/outputs/aar/unity-ads-release.aar") + artifact androidSourcesJar + artifact androidJavadocsJar + + pom { + name = ARTIFACT_ID + description = 'Monetize your entire player base and reach new audiences with video ads.' + url = 'https://github.com/Unity-Technologies/unity-ads-android' + licenses { + license { + name = 'Unity Ads License' + url = 'https://unity3d.com/legal/monetization-services-terms-of-service' + } + } + + scm { + connection = 'https://github.com/Unity-Technologies/unity-ads-android.git' + developerConnection = 'https://github.com/Unity-Technologies/unity-ads-android.git' + url = 'https://github.com/Unity-Technologies/unity-ads-android' + } + developers { + developer { + id = 'Unity Ads' + name = 'Unity Ads' + email = 'ads-sdk@unity3d.com' + } + } + } + } + } +} + +signing { + required { SIGN_AAR } + sign publishing.publications.release +} \ No newline at end of file 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 7100d461..5115f25d 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 @@ -13,6 +13,7 @@ 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.WebViewBridgeInvocationRunnableTests; 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; @@ -33,6 +34,7 @@ BannerViewCacheTests.class, WebViewBridgeSharedObjectTests.class, WebViewBridgeInvocationTests.class, + WebViewBridgeInvocationRunnableTests.class, LoadOperationTests.class, LoadModuleTests.class, LoadModuleDecoratorTimeoutTests.class, 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 index 371cfb76..dd290ec2 100644 --- 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 @@ -62,7 +62,7 @@ public void executeAdOperationCallsOnUnityAdsFailedToLoadWhenPlacementNotSet() { loadModule.executeAdOperation(webViewBridgeInvokerMock, loadOperationState); TestUtilities.SleepCurrentThread(uiThreadDelay); - Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad(null, UnityAds.UnityAdsLoadError.INVALID_ARGUMENT,"[UnityAds] Placement ID cannot be null"); + Mockito.verify(loadListenerMock, times(1)).onUnityAdsFailedToLoad("", UnityAds.UnityAdsLoadError.INVALID_ARGUMENT,"[UnityAds] Placement ID cannot be null"); } @Test diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleDecoratorTimeoutTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleDecoratorTimeoutTests.java new file mode 100644 index 00000000..6d35cb3f --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/ads/operation/ShowModuleDecoratorTimeoutTests.java @@ -0,0 +1,85 @@ +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.ShowModuleDecoratorTimeout; +import com.unity3d.services.ads.operation.show.ShowOperationState; +import com.unity3d.services.core.request.ISDKMetricSender; +import com.unity3d.services.core.webview.bridge.CallbackStatus; +import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocation; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocationRunnable; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mockito; + +import java.lang.reflect.Method; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +public class ShowModuleDecoratorTimeoutTests { + private static String placementId = "TestPlacementId"; + private static UnityAdsShowOptions showOptions = new UnityAdsShowOptions(); + private ISDKMetricSender sdkMetricSender; + + + private static int showTimeout = 50; + private static int uiThreadDelay = 150; + + private IUnityAdsShowListener showListenerMock; + private IShowModule showModule; + + @Rule + public final ActivityTestRule _activityRule = new ActivityTestRule<>(InstrumentationTestActivity.class); + + @Before + public void beforeEachTest() { + showListenerMock = mock(IUnityAdsShowListener.class); + sdkMetricSender = mock(ISDKMetricSender.class); + // We need a real instance since ShowModule will create the Operation object (which holds the State with Listener ID) + showModule = new ShowModule(sdkMetricSender); + } + + @After + public void afterEachTest() { + //Allow for any timeout threads to complete before starting the next test to prevent inaccurate mock counts + TestUtilities.SleepCurrentThread(showTimeout); + } + + @Test + public void testShowModuleDecoratorTimeout() { + ShowModuleDecoratorTimeout showModuleDecoratorTimeout = new ShowModuleDecoratorTimeout(showModule); + ShowOperationState showOperationState = new ShowOperationState(placementId, showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + showModuleDecoratorTimeout.executeAdOperation(mock(IWebViewBridgeInvoker.class), showOperationState); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(showListenerMock, times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "[UnityAds] Timeout while trying to show TestPlacementId"); + } + + @Test + public void testShowModuleDecoratorShowConsentNoTimeout() { + ShowModuleDecoratorTimeout showModuleDecoratorTimeout = new ShowModuleDecoratorTimeout(showModule); + ShowOperationState showOperationState = new ShowOperationState(placementId, showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout)); + IWebViewBridgeInvoker webViewBridgeInvoker = mock(IWebViewBridgeInvoker.class); + when(webViewBridgeInvoker.invokeMethod(anyString(), anyString(), any(Method.class), any())).thenReturn(true); + + showModuleDecoratorTimeout.executeAdOperation(webViewBridgeInvoker, showOperationState); + WebViewBridgeInvocationRunnable.onInvocationComplete(CallbackStatus.OK); + showModuleDecoratorTimeout.onUnityAdsShowConsent(showOperationState.getId()); + TestUtilities.SleepCurrentThread(uiThreadDelay); + Mockito.verify(showListenerMock, times(0)).onUnityAdsShowFailure(anyString(), any(UnityAds.UnityAdsShowError.class), anyString()); + } +} 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 index 639d7c92..a467c1b8 100644 --- 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 @@ -62,7 +62,7 @@ public void executeAdOperationCallsOnUnityAdsFailedToShowWhenPlacementNotSet() { _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); TestUtilities.SleepCurrentThread(uiThreadDelay); - Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(null, UnityAds.UnityAdsShowError.INVALID_ARGUMENT, "[UnityAds] Placement ID cannot be null"); + Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure("", UnityAds.UnityAdsShowError.INVALID_ARGUMENT, "[UnityAds] Placement ID cannot be null"); } @Test @@ -77,7 +77,7 @@ public Object answer(InvocationOnMock invocation) { _showModule.executeAdOperation(_webViewBridgeInvokerMock, showOperationState); TestUtilities.SleepCurrentThread(uiThreadDelay); - Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "WebViewBridgeInvocation:execute: invokeMethod failure"); + Mockito.verify((_showListenerMock), times(1)).onUnityAdsShowFailure(placementId, UnityAds.UnityAdsShowError.INTERNAL_ERROR, "WebViewBridgeInvocationRunnable:run: invokeMethod failure"); Mockito.verify(_sdkMetricSender, times(1)).SendSDKMetricEventWithTag(SDKMetricEvents.native_show_callback_error, new HashMap (){{ put("cbs", "invocationFailure"); }}); diff --git a/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnableTests.java b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnableTests.java new file mode 100644 index 00000000..fa478efc --- /dev/null +++ b/unity-ads/src/androidTest/java/com/unity3d/ads/test/instrumentation/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnableTests.java @@ -0,0 +1,115 @@ +package com.unity3d.ads.test.instrumentation.services.core.webview.bridge.invocation; + +import com.unity3d.ads.test.TestUtilities; +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.WebViewBridgeInvocationRunnable; + +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 WebViewBridgeInvocationRunnableTests { + 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; + private WebViewBridgeInvocationRunnable runnable; + + @Before + public void beforeEachTest() { + webViewBridgeInvokerMock = mock(IWebViewBridgeInvoker.class); + invocationCallbackMock = mock(IWebViewBridgeInvocationCallback.class); + } + + @After + public void afterEachTest() { + TestUtilities.SleepCurrentThread(invocationTimeout); + } + + @Test + public void runCallsInvocationFailureWhenWebViewBridgeInvokerFails() { + runnable = new WebViewBridgeInvocationRunnable(invocationCallbackMock, webViewBridgeInvokerMock, className, methodName, invocationTimeout, params); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + return false; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + runnable.run(); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 0, 1, 0, "WebViewBridgeInvocationRunnable:run: invokeMethod failure", null); + } + + @Test + public void runCallsInvocationSuccessWhenCallbackStatusOk() { + runnable = new WebViewBridgeInvocationRunnable(invocationCallbackMock, webViewBridgeInvokerMock, className, methodName, invocationTimeout, params); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + runnable.onInvocationComplete(CallbackStatus.OK); + return true; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + runnable.run(); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 1, 0, 0); + } + + @Test + public void runCallsInvocationFailureWhenCallbackStatusNotOk() { + runnable = new WebViewBridgeInvocationRunnable(invocationCallbackMock, webViewBridgeInvokerMock, className, methodName, invocationTimeout, params); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + runnable.onInvocationComplete(CallbackStatus.ERROR); + return true; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + runnable.run(); + TestUtilities.SleepCurrentThread(uiThreadDelay); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 0, 1, 0, "WebViewBridgeInvocationRunnable:run CallbackStatus.Error", CallbackStatus.ERROR); + } + + @Test + public void runCallsInvocationTimeoutWhenResponseTimeoutIsReached() { + runnable = new WebViewBridgeInvocationRunnable(invocationCallbackMock, webViewBridgeInvokerMock, className, methodName, 10, params); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + return true; + }}).when(webViewBridgeInvokerMock).invokeMethod(anyString(), anyString(), any(Method.class), any(Object.class)); + + runnable.run(); + TestUtilities.SleepCurrentThread(100); + + VerifyInvocationCallbackMockCalls(invocationCallbackMock, 0, 0, 1); + } + + 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(); + } +} 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 index 1c298233..017a4708 100644 --- 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 @@ -4,20 +4,15 @@ 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 com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocationRunnable; 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.concurrent.ExecutorService; 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; @@ -26,7 +21,6 @@ public class WebViewBridgeInvocationTests { 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; @@ -43,91 +37,11 @@ public void afterEachTest() { } @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)); + public void invokeCallsWebViewBridgeInvocationSingleThreadedExecutorSubmit() { + ExecutorService executorMock = mock(ExecutorService.class); + final WebViewBridgeInvocation webViewBridgeInvocation = new WebViewBridgeInvocation(executorMock, webViewBridgeInvokerMock, invocationCallbackMock); 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(); + Mockito.verify(executorMock, times(1)).submit(any(WebViewBridgeInvocationRunnable.class)); } -} \ No newline at end of file +} 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 index 1044e20b..e3fa49c3 100644 --- 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 @@ -13,6 +13,13 @@ public static void sendShowFailedEvent(final String placementId, final String li callback.invoke(); } + @WebViewExposed + public static void sendShowConsentEvent(final String placementId, final String listenerId, WebViewCallback callback) { + ShowModule.getInstance().onUnityAdsShowConsent(listenerId); + callback.invoke(); + } + + @WebViewExposed public static void sendShowStartEvent(final String placementId, final String listenerId, WebViewCallback callback) { ShowModule.getInstance().onUnityAdsShowStart(listenerId); 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 index 98eb51f9..247e0145 100644 --- 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 @@ -1,16 +1,20 @@ 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; +import com.unity3d.services.core.webview.bridge.invocation.WebViewBridgeInvocationSingleThreadedExecutor; + +import java.util.concurrent.ExecutorService; public abstract class AdModule extends WebViewBridgeSharedObjectStore implements IAdModule { protected ISDKMetricSender _sdkMetricSender; + protected ExecutorService _executorService; protected AdModule(ISDKMetricSender sdkMetricSender) { super(); _sdkMetricSender = sdkMetricSender; + _executorService = WebViewBridgeInvocationSingleThreadedExecutor.getInstance().getExecutorService(); } public ISDKMetricSender getMetricSender() { 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 index 4dd8fcbb..ae68f951 100644 --- 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 @@ -2,8 +2,6 @@ import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocation; -import java.util.UUID; - public abstract class AdOperation implements IAdOperation { private static String invocationClassName = "webview"; diff --git a/unity-ads/src/main/java/com/unity3d/services/ads/operation/OperationState.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/OperationState.java new file mode 100644 index 00000000..327534c7 --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/ads/operation/OperationState.java @@ -0,0 +1,29 @@ +package com.unity3d.services.ads.operation; + +import android.os.ConditionVariable; + +import com.unity3d.services.core.configuration.Configuration; +import com.unity3d.services.core.webview.bridge.IWebViewSharedObject; + +import java.util.UUID; + +public class OperationState implements IWebViewSharedObject { + private static String _emptyPlacementId = ""; + + public String id; + public String placementId; + public Configuration configuration; + public ConditionVariable timeoutCV; + + public OperationState(String placementId, Configuration configuration) { + this.placementId = placementId == null ? _emptyPlacementId : 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/load/LoadModule.java b/unity-ads/src/main/java/com/unity3d/services/ads/operation/load/LoadModule.java index bc3f9ce4..d701b643 100644 --- 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 @@ -14,6 +14,7 @@ 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.invocation.WebViewBridgeInvocationSingleThreadedExecutor; import org.json.JSONException; import org.json.JSONObject; @@ -49,7 +50,7 @@ public void executeAdOperation(final IWebViewBridgeInvoker webViewBridgeInvoker, return; } - LoadOperation loadOperation = new LoadOperation(state, new WebViewBridgeInvocation(webViewBridgeInvoker, new IWebViewBridgeInvocationCallback() { + LoadOperation loadOperation = new LoadOperation(state, new WebViewBridgeInvocation(_executorService, webViewBridgeInvoker, new IWebViewBridgeInvocationCallback() { @Override public void onSuccess() { @@ -73,12 +74,14 @@ public void onTimeout() { } })); - JSONObject invocationParameters = new JSONObject(); + JSONObject parameters = new JSONObject(); + JSONObject options = new JSONObject(); try { - invocationParameters.put("listenerId", loadOperation.getId()); - invocationParameters.put("placementId", state.placementId); - invocationParameters.put("time", Device.getElapsedRealtime()); - invocationParameters.put("options", state.loadOptions.getData()); + options.put("headerBiddingOptions", state.loadOptions.getData()); + parameters.put("options", options); + parameters.put("listenerId", loadOperation.getId()); + parameters.put("placementId", state.placementId); + parameters.put("time", Device.getElapsedRealtime()); } catch (JSONException e) { sendOnUnityAdsFailedToLoad(state, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, errorMsgFailedToCreateLoadRequest); return; @@ -88,7 +91,7 @@ public void onTimeout() { } set(loadOperation); - loadOperation.invoke(state.configuration.getWebViewBridgeTimeout(), invocationParameters); + loadOperation.invoke(state.configuration.getWebViewBridgeTimeout(), parameters); } public void onUnityAdsAdLoaded(String operationId) { 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 index 07232c57..c38fdcc0 100644 --- 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 @@ -5,7 +5,6 @@ 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; 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 index 10de3cdc..7ef382c2 100644 --- 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 @@ -1,33 +1,17 @@ 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.ads.operation.OperationState; import com.unity3d.services.core.configuration.Configuration; -import java.util.UUID; - -public class LoadOperationState implements IWebViewSharedObject { - public String id; +public class LoadOperationState extends OperationState { 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) { + super(placementId, 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 index 68a97f65..50f9a291 100644 --- 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 @@ -5,6 +5,7 @@ public interface IShowModule extends IAdModule { void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message); + void onUnityAdsShowConsent(String id); void onUnityAdsShowStart(String id); void onUnityAdsShowClick(String id); void onUnityAdsShowComplete(String id, UnityAds.UnityAdsShowCompletionState state); 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 index 52068dd2..b59af5e1 100644 --- 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 @@ -18,6 +18,7 @@ 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.invocation.WebViewBridgeInvocationSingleThreadedExecutor; import org.json.JSONException; import org.json.JSONObject; @@ -46,7 +47,7 @@ public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, final sendOnUnityAdsFailedToShow(state, errorMsgPlacementIdNull, UnityAds.UnityAdsShowError.INVALID_ARGUMENT); return; } - IShowOperation showOperation = new ShowOperation(state, new WebViewBridgeInvocation(webViewBridgeInvoker, new IWebViewBridgeInvocationCallback() { + IShowOperation showOperation = new ShowOperation(state, new WebViewBridgeInvocation(_executorService, webViewBridgeInvoker, new IWebViewBridgeInvocationCallback() { @Override public void onSuccess() { } @@ -72,11 +73,12 @@ public void onTimeout() { ClientProperties.setActivity(state.activity); Display defaultDisplay = ((WindowManager)state.activity.getSystemService(state.activity.WINDOW_SERVICE)).getDefaultDisplay(); + JSONObject parameters = new JSONObject(); JSONObject options = new JSONObject(); JSONObject display = new JSONObject(); try { - options.put("requestedOrientation", state.activity.getRequestedOrientation()); + display.put("requestedOrientation", state.activity.getRequestedOrientation()); display.put("rotation", defaultDisplay.getRotation()); if (Build.VERSION.SDK_INT >= 13) { Point displaySize = new Point(); @@ -88,10 +90,11 @@ public void onTimeout() { 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()); + options.put("headerBiddingOptions", state.showOptions.getData()); + parameters.put("options", options); + parameters.put("listenerId", showOperation.getId()); + parameters.put("placementId", state.placementId); + parameters.put("time", Device.getElapsedRealtime()); } catch (JSONException e) { sendOnUnityAdsFailedToShow(state, "[UnityAds] Error creating show options", UnityAds.UnityAdsShowError.INTERNAL_ERROR); return; @@ -101,7 +104,7 @@ public void onTimeout() { } set(showOperation); - showOperation.invoke(state.configuration.getWebViewBridgeTimeout(), options); + showOperation.invoke(state.configuration.getWebViewBridgeTimeout(), parameters); } public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message) { @@ -111,6 +114,12 @@ public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, S remove(id); } + public void onUnityAdsShowConsent(String id) { + final IShowOperation showOperation = get(id); + if (showOperation == null || showOperation.getShowOperationState() == null) return; + // We do nothing for now since we don't report back to the user API + } + public void onUnityAdsShowStart(String id) { final IShowOperation showOperation = get(id); if (showOperation == null || showOperation.getShowOperationState() == null) return; 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 index 80ffa116..71fecc70 100644 --- 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 @@ -26,6 +26,9 @@ public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, S _showModule.onUnityAdsShowFailure(id, error, message); } + @Override + public void onUnityAdsShowConsent(String id) { _showModule.onUnityAdsShowConsent(id);} + @Override public void onUnityAdsShowStart(String id) { _showModule.onUnityAdsShowStart(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 index 0a57dd65..13244c9a 100644 --- 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 @@ -36,6 +36,12 @@ public void run() { }); } + @Override + public void onUnityAdsShowConsent(String id) { + releaseOperationTimeoutLock(id); + super.onUnityAdsShowConsent(id); + } + @Override public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message) { releaseOperationTimeoutLock(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 index 69a3a649..14e96ba8 100644 --- 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 @@ -1,36 +1,21 @@ 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.ads.operation.OperationState; 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 class ShowOperationState extends OperationState { 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) { + super(placementId, 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/ads/video/VideoPlayerView.java b/unity-ads/src/main/java/com/unity3d/services/ads/video/VideoPlayerView.java index 13dc9a97..e7ed7d6b 100644 --- a/unity-ads/src/main/java/com/unity3d/services/ads/video/VideoPlayerView.java +++ b/unity-ads/src/main/java/com/unity3d/services/ads/video/VideoPlayerView.java @@ -154,11 +154,15 @@ public void onCompletion(MediaPlayer mp) { } }); - start(); - stopVideoProgressTimer(); - startVideoProgressTimer(); + try { + start(); + stopVideoProgressTimer(); + startVideoProgressTimer(); - WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.VIDEOPLAYER, VideoPlayerEvent.PLAY, _videoUrl); + WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.VIDEOPLAYER, VideoPlayerEvent.PLAY, _videoUrl); + } catch (IllegalStateException ex) { + WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.VIDEOPLAYER, VideoPlayerEvent.ILLEGAL_STATE, _videoUrl, false); + } } public void setInfoListenerEnabled (boolean enabled) { 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 index b68f5ff5..b43f00f3 100644 --- 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 @@ -1,25 +1,17 @@ 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; + private ExecutorService _executorService; - public WebViewBridgeInvocation(IWebViewBridgeInvoker webViewBridgeInvoker, IWebViewBridgeInvocationCallback invocationCallback) { - responseTimeout = new ConditionVariable(); + public WebViewBridgeInvocation(ExecutorService _executorService, IWebViewBridgeInvoker webViewBridgeInvoker, IWebViewBridgeInvocationCallback invocationCallback) { + this._executorService = _executorService; this.invocationCallback = invocationCallback; if (webViewBridgeInvoker == null) { @@ -27,50 +19,11 @@ public WebViewBridgeInvocation(IWebViewBridgeInvoker webViewBridgeInvoker, IWebV } _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; - } + public synchronized void invoke(final String className, final String methodName, final int timeoutLengthInMilliSeconds, final Object... invocationParameters) { + _executorService.submit( + new WebViewBridgeInvocationRunnable(invocationCallback, _webViewBridgeInvoker, className, methodName, timeoutLengthInMilliSeconds, invocationParameters)); } -} \ No newline at end of file +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnable.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnable.java new file mode 100644 index 00000000..d7dd31db --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationRunnable.java @@ -0,0 +1,69 @@ +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; + +public class WebViewBridgeInvocationRunnable implements Runnable { + private static ConditionVariable _responseTimeout; + private static CallbackStatus _callbackStatus; + + private IWebViewBridgeInvocationCallback _invocationCallback; + private IWebViewBridgeInvoker _webViewBridgeInvoker; + private Method _webViewBridgeCallbackMethod; + + private String _className; + private String _methodName; + private int _timeoutLengthInMilliSeconds; + private Object[] _invocationParameters; + + public WebViewBridgeInvocationRunnable(IWebViewBridgeInvocationCallback invocationCallback, IWebViewBridgeInvoker webViewBridgeInvoker, String className, String methodName, int timeoutLengthInMilliSeconds, Object...invocationParameters) { + try { + _webViewBridgeCallbackMethod = WebViewBridgeInvocationRunnable.class.getMethod("onInvocationComplete", CallbackStatus.class); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("WebViewBridgeInvocation callback method cannot be found", e); + } + + _invocationCallback = invocationCallback; + _webViewBridgeInvoker = webViewBridgeInvoker; + _className = className; + _methodName = methodName; + _timeoutLengthInMilliSeconds = timeoutLengthInMilliSeconds; + _invocationParameters = invocationParameters; + } + + @Override + public void run() { + _callbackStatus = null; + _responseTimeout = new ConditionVariable(); + + boolean invokeMethodSuccess = _webViewBridgeInvoker.invokeMethod(_className, _methodName, _webViewBridgeCallbackMethod, _invocationParameters); + + if (_invocationCallback == null) return; + + if (!invokeMethodSuccess) { + _invocationCallback.onFailure("WebViewBridgeInvocationRunnable:run: invokeMethod failure", null); + return; + } + + if (_responseTimeout.block(_timeoutLengthInMilliSeconds)) { + if (_callbackStatus == CallbackStatus.OK) { + _invocationCallback.onSuccess(); + } else { + _invocationCallback.onFailure("WebViewBridgeInvocationRunnable:run CallbackStatus.Error", _callbackStatus); + } + } else { + _invocationCallback.onTimeout(); + } + } + + public static synchronized void onInvocationComplete(CallbackStatus status) { + _callbackStatus = status; + if (_responseTimeout != null) { + _responseTimeout.open(); + } + } +} diff --git a/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationSingleThreadedExecutor.java b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationSingleThreadedExecutor.java new file mode 100644 index 00000000..22e96cea --- /dev/null +++ b/unity-ads/src/main/java/com/unity3d/services/core/webview/bridge/invocation/WebViewBridgeInvocationSingleThreadedExecutor.java @@ -0,0 +1,24 @@ +package com.unity3d.services.core.webview.bridge.invocation; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class WebViewBridgeInvocationSingleThreadedExecutor { + private static WebViewBridgeInvocationSingleThreadedExecutor instance; + private ExecutorService _ExecutorService; + + public static WebViewBridgeInvocationSingleThreadedExecutor getInstance() { + if (instance == null) { + instance = new WebViewBridgeInvocationSingleThreadedExecutor(); + } + return instance; + } + + private WebViewBridgeInvocationSingleThreadedExecutor() { + _ExecutorService = Executors.newSingleThreadExecutor(); + } + + public ExecutorService getExecutorService() { + return _ExecutorService; + } +}