This commit is contained in:
alex-crowe 2021-07-13 13:48:25 -04:00
Родитель 618ce50538
Коммит 73eee20f1e
122 изменённых файлов: 4156 добавлений и 30 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -7,6 +7,7 @@ captures
testServerAddress.txt
app/bin
unity-ads/bin
libs
javadoc
.settings
.project

Просмотреть файл

@ -37,16 +37,16 @@ test-local-staging-localhost: device-connected wake-up-device push-test-server-a
run-all-tests: test-instrumentation test-legacy test-integration
test-ci:
./gradlew connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.InstrumentationTestSuite,com.unity3d.ads.test.LegacyTestSuite
./gradlew unity-ads:connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.InstrumentationTestSuite,com.unity3d.ads.test.LegacyTestSuite
test-instrumentation:
./gradlew connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.InstrumentationTestSuite
./gradlew unity-ads:connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.InstrumentationTestSuite
test-legacy:
./gradlew connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.LegacyTestSuite
./gradlew unity-ads:connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.LegacyTestSuite
test-integration:
./gradlew connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.IntegrationTestSuite
./gradlew unity-ads:connectedDebugAndroidTest -i -w --stacktrace -Pandroid.testInstrumentationRunnerArguments.class=com.unity3d.ads.test.IntegrationTestSuite
push-test-server-address-ip:
echo http://$(shell ifconfig |grep "inet" |grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" |grep -v -E "^0|^127" -m 1):8080 > testServerAddress.txt

Просмотреть файл

@ -7,8 +7,8 @@ android {
applicationId "com.unity3d.ads.example"
minSdkVersion 19
targetSdkVersion 30
versionCode = 3720
versionName = "3.7.2"
versionCode = 3740
versionName = "3.7.4"
}
flavorDimensions "arEnabled"
@ -27,18 +27,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
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'
implementation "com.google.android.material:material:1.3.0"
implementation "androidx.appcompat:appcompat:1.3.0"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
arImplementation 'com.google.ar:core:1.4.0'
implementation 'androidx.transition:transition:1.4.1'
implementation project(':unity-ads')
}

Двоичные данные
app/src/main/res/drawable/unityads_logo.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 6.5 KiB

После

Ширина:  |  Высота:  |  Размер: 2.3 KiB

Просмотреть файл

@ -1,19 +1,18 @@
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
buildscript {
repositories {
jcenter()
gradlePluginPortal()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:4.2.1'
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'

2
gradle/wrapper/gradle-wrapper.properties поставляемый
Просмотреть файл

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip

Просмотреть файл

@ -1 +1,5 @@
include ':unity-scaradapter-1920'
include ':unity-scaradapter-1950'
include ':unity-scaradapter-2000'
include ':unity-scaradapter-common'
include ':app', ':unity-ads'

Просмотреть файл

@ -19,8 +19,8 @@ dependencies {
ext {
GROUP_ID = "com.unity3d.ads"
ARTIFACT_ID = "unity-ads"
VERSION_ID = "3.7.2"
VERSION_CODE = 3720
VERSION_ID = "3.7.4"
VERSION_CODE = 3740
SIGN_AAR = properties.getProperty("SIGN_AAR") ?: false
}
@ -47,6 +47,7 @@ android {
buildConfigField('int', 'VERSION_CODE', "$versionCode")
buildConfigField('String', 'VERSION_NAME', "\"$versionName\"")
testBuildType "debug"
multiDexEnabled true
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
testInstrumentationRunnerArguments disableAnalytics: 'true' // Won't work yet, see: https://code.google.com/p/android/issues/detail?id=188241
@ -71,6 +72,25 @@ android {
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation project(':unity-scaradapter-2000')
androidTestImplementation project(':unity-scaradapter-1950')
androidTestImplementation project(':unity-scaradapter-1920')
androidTestImplementation project(':unity-scaradapter-common')
androidTestImplementation 'org.mockito:mockito-core:2.28.2'
androidTestImplementation 'org.mockito:mockito-android:2.25.0'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'com.google.android.gms:play-services-ads:19.5.0'
compileOnly 'com.google.ar:core:1.0.0'
compileOnly project(':unity-scaradapter-2000')
compileOnly project(':unity-scaradapter-1950')
compileOnly project(':unity-scaradapter-1920')
compileOnly project(':unity-scaradapter-common')
}
task javadoc(type: Javadoc) {
description "Generates Javadoc for Release"
source = android.sourceSets.main.java.srcDirs

Просмотреть файл

@ -35,6 +35,10 @@
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true" />
<!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>
</application>
</manifest>

Просмотреть файл

@ -1,5 +1,6 @@
package com.unity3d.ads.test;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.GmaScarTestSuite;
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;
@ -28,7 +29,6 @@ import org.junit.runners.Suite;
AcquisitionTypeTest.class,
AdsPropertiesTests.class,
InitializationNotificationCenterTest.class,
// LoadBridgeTest.class,
WebPlayerViewSettingsCacheTest.class,
WebPlayerViewCacheTest.class,
BannerViewCacheTests.class,
@ -42,7 +42,8 @@ import org.junit.runners.Suite;
LoadModuleDecoratorInitializationBufferTests.class,
AdOperationTests.class,
ShowModuleTests.class,
ConfigurationTest.class
ConfigurationTest.class,
GmaScarTestSuite.class
})
public class InstrumentationTestSuite {}

Просмотреть файл

@ -0,0 +1,27 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.adapters.ScarAdapterFactoryTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.finder.GMAInitializerTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.finder.PresenceDetectorTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.finder.ScarVersionFinderTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges.AdapterStatusBridgeTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges.InitializationStatusBridgeTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges.InitializeListenerBridgeTest;
import com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges.MobileAdsBridgeTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
ScarAdapterFactoryTest.class,
GMAInitializerTest.class,
PresenceDetectorTest.class,
ScarVersionFinderTest.class,
AdapterStatusBridgeTest.class,
InitializationStatusBridgeTest.class,
InitializeListenerBridgeTest.class,
MobileAdsBridgeTest.class
})
public class GmaScarTestSuite {
}

Просмотреть файл

@ -0,0 +1,43 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.adapters;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.services.ads.gmascar.adapters.ScarAdapterFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ScarAdapterFactoryTest {
@Mock
private IAdsErrorHandler adsErrorHandlerMock;
private ScarAdapterFactory _scarAdapterFactory = new ScarAdapterFactory();
@Test
public void testScarAdapterFactory1920() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(ScarAdapterFactory.CODE_19_2, adsErrorHandlerMock);
Assert.assertTrue(adapter instanceof com.unity3d.scar.adapter.v1920.ScarAdapter);
}
@Test
public void testScarAdapterFactory1950() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(ScarAdapterFactory.CODE_19_5, adsErrorHandlerMock);
Assert.assertTrue(adapter instanceof com.unity3d.scar.adapter.v1950.ScarAdapter);
}
@Test
public void testScarAdapterFactory2000() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(ScarAdapterFactory.CODE_20_0, adsErrorHandlerMock);
Assert.assertTrue(adapter instanceof com.unity3d.scar.adapter.v2000.ScarAdapter);
}
@Test
public void testScarAdapterFactoryUnsupported() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(-1, adsErrorHandlerMock);
Assert.assertNull(adapter);
}
}

Просмотреть файл

@ -0,0 +1,19 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges;
import com.google.android.gms.ads.initialization.AdapterStatus;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import org.junit.Assert;
import org.junit.Test;
public class AdapterStatusBridgeTest {
@Test
public void testAdapterStatusBridge() {
AdapterStatusBridge adapterStatusBridge = new AdapterStatusBridge();
Object[] statesEnum = adapterStatusBridge.getAdapterStatesEnum();
Assert.assertEquals(AdapterStatus.State.values().length, statesEnum.length);
Assert.assertEquals(AdapterStatus.State.NOT_READY, statesEnum[0]);
Assert.assertEquals(AdapterStatus.State.READY, statesEnum[1]);
}
}

Просмотреть файл

@ -0,0 +1,18 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
import org.junit.Assert;
import org.junit.Test;
import java.util.Map;
public class InitializationStatusBridgeTest {
@Test
public void testInitializationStatusBridgeNotInitialized() {
InitializationStatusBridge initializationStatusBridge = new InitializationStatusBridge();
Map<String, Object> adapterStatusMap = initializationStatusBridge.getAdapterStatusMap(new Object());
Assert.assertNull(adapterStatusMap);
}
}

Просмотреть файл

@ -0,0 +1,16 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import org.junit.Assert;
import org.junit.Test;
public class InitializeListenerBridgeTest {
@Test
public void testInitializeListenerBridge() {
InitializeListenerBridge initializeListenerBridge = new InitializeListenerBridge();
Object listenerProxy = initializeListenerBridge.createInitializeListenerProxy();
Assert.assertNotNull(listenerProxy);
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.bridges;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.timeout;
public class MobileAdsBridgeTest {
@Test
@Ignore("Have to ignore for now cause the bridge is stateful (underlying call to GMA static init depends on other tests")
public void testMobileAdsBridgeGetVersionNotInitialized() {
MobileAdsBridge mobileAdsBridge = new MobileAdsBridge();
String versionString = mobileAdsBridge.getVersionString();
Assert.assertEquals("0.0.0", versionString);
}
@Test
public void testMobileAdsBridgeGetVersion() {
OnInitializationCompleteListener initializationCompleteListener = Mockito.mock(OnInitializationCompleteListener.class);
MobileAdsBridge mobileAdsBridge = new MobileAdsBridge();
mobileAdsBridge.initialize(InstrumentationRegistry.getInstrumentation().getContext(), initializationCompleteListener);
Mockito.verify(initializationCompleteListener, timeout(1000).times(1)).onInitializationComplete(Mockito.any(InitializationStatus.class));
String versionString = mobileAdsBridge.getVersionString();
Assert.assertTrue(String.format("Minor version 203404000 is not found in %s", versionString), versionString.contains("203404000"));
}
@Test
@Ignore("Cannot test this case since the underlying GMA call is static so test ordering impacts this result.")
public void testMobileAdsBridgeGetInitStatusNotInitialized() {
MobileAdsBridge mobileAdsBridge = new MobileAdsBridge();
Object initializationStatus = mobileAdsBridge.getInitializationStatus();
Assert.assertNull(initializationStatus);
}
@Test
public void testMobileAdsBridgeGetInitStatus() {
OnInitializationCompleteListener initializationCompleteListener = Mockito.mock(OnInitializationCompleteListener.class);
MobileAdsBridge mobileAdsBridge = new MobileAdsBridge();
mobileAdsBridge.initialize(InstrumentationRegistry.getInstrumentation().getContext(), initializationCompleteListener);
Object initializationStatus = mobileAdsBridge.getInitializationStatus();
Assert.assertTrue(InitializationStatus.class.isAssignableFrom(initializationStatus.getClass()));
}
}

Просмотреть файл

@ -0,0 +1,61 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.finder;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import com.unity3d.services.ads.gmascar.finder.GMAInitializer;
import com.unity3d.services.core.webview.WebView;
import com.unity3d.services.core.webview.WebViewApp;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@RunWith(MockitoJUnitRunner.class)
public class GMAInitializerTest {
@Mock
MobileAdsBridge mobileAdsBridge;
@Mock
InitializeListenerBridge initializeListenerBridge;
@Mock
InitializationStatusBridge initializationStatusBridge;
@Mock
AdapterStatusBridge adapterStatusBridge;
@Test
public void testGmaInitializer() {
GMAInitializer gmaInitializer = new GMAInitializer(mobileAdsBridge, initializeListenerBridge, initializationStatusBridge, adapterStatusBridge);
gmaInitializer.initializeGMA();
Mockito.verify(mobileAdsBridge, times(1)).initialize(Mockito.any(Context.class), Mockito.any());
}
@Test
public void testGmaInitializerInitSuccess() {
MobileAdsBridge realMobileAdsBridge = new MobileAdsBridge();
OnInitializationCompleteListener initializationCompleteListener = Mockito.mock(OnInitializationCompleteListener.class);
realMobileAdsBridge.initialize(InstrumentationRegistry.getInstrumentation().getContext(), initializationCompleteListener);
Object initializationStatus = realMobileAdsBridge.getInitializationStatus();
InitializationStatusBridge realStatusBridge = new InitializationStatusBridge();
GMAInitializer gmaInitializer = new GMAInitializer(realMobileAdsBridge, initializeListenerBridge, realStatusBridge, adapterStatusBridge);
WebViewApp mockWebViewApp = Mockito.mock(WebViewApp.class);
Mockito.when(mockWebViewApp.sendEvent(Mockito.any(Enum.class), Mockito.any(Enum.class))).thenReturn(true);
WebViewApp.setCurrentApp(mockWebViewApp);
Mockito.when(adapterStatusBridge.isGMAInitialized(Mockito.any())).thenReturn(true);
boolean initSuccess = gmaInitializer.initSuccessful(initializationStatus);
Assert.assertTrue(initSuccess);
}
}

Просмотреть файл

@ -0,0 +1,76 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.finder;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import com.unity3d.services.ads.gmascar.finder.PresenceDetector;
import com.unity3d.services.ads.gmascar.finder.ScarVersionFinder;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PresenceDetectorTest {
@Mock
MobileAdsBridge mobileAdsBridgeMock;
@Mock
InitializeListenerBridge initializeListenerBridgeMock;
@Mock
InitializationStatusBridge initializationStatusBridgeMock;
@Mock
AdapterStatusBridge adapterStatusBridgeMock;
@Before
public void setup() {
Mockito.when(mobileAdsBridgeMock.exists()).thenReturn(true);
Mockito.when(initializeListenerBridgeMock.exists()).thenReturn(true);
Mockito.when(initializationStatusBridgeMock.exists()).thenReturn(true);
Mockito.when(adapterStatusBridgeMock.exists()).thenReturn(true);
}
@Test
public void testScarPresenceDetector() {
PresenceDetector scarPresenceDetector = new PresenceDetector(mobileAdsBridgeMock, initializeListenerBridgeMock, initializationStatusBridgeMock, adapterStatusBridgeMock);
Assert.assertTrue(scarPresenceDetector.areGMAClassesPresent());
}
@Test
public void testScarPresenceDetectorWithNullBridges() {
PresenceDetector scarPresenceDetector = new PresenceDetector(null, null, null, null);
Assert.assertFalse(scarPresenceDetector.areGMAClassesPresent());
}
@Test
public void testScarPresenceDetectorMobileAdsClassMissing() {
Mockito.when(mobileAdsBridgeMock.exists()).thenReturn(false);
PresenceDetector scarPresenceDetector = new PresenceDetector(mobileAdsBridgeMock, initializeListenerBridgeMock, initializationStatusBridgeMock, adapterStatusBridgeMock);
Assert.assertFalse(scarPresenceDetector.areGMAClassesPresent());
}
@Test
public void testScarPresenceDetectorListenerClassMissing() {
Mockito.when(initializeListenerBridgeMock.exists()).thenReturn(false);
PresenceDetector scarPresenceDetector = new PresenceDetector(mobileAdsBridgeMock, initializeListenerBridgeMock, initializationStatusBridgeMock, adapterStatusBridgeMock);
Assert.assertFalse(scarPresenceDetector.areGMAClassesPresent());
}
@Test
public void testScarPresenceDetectorInitStatusClassMissing() {
Mockito.when(initializationStatusBridgeMock.exists()).thenReturn(false);
PresenceDetector scarPresenceDetector = new PresenceDetector(mobileAdsBridgeMock, initializeListenerBridgeMock, initializationStatusBridgeMock, adapterStatusBridgeMock);
Assert.assertFalse(scarPresenceDetector.areGMAClassesPresent());
}
@Test
public void testScarPresenceDetectorAdapterStatusClassMissing() {
Mockito.when(adapterStatusBridgeMock.exists()).thenReturn(false);
PresenceDetector scarPresenceDetector = new PresenceDetector(mobileAdsBridgeMock, initializeListenerBridgeMock, initializationStatusBridgeMock, adapterStatusBridgeMock);
Assert.assertFalse(scarPresenceDetector.areGMAClassesPresent());
}
}

Просмотреть файл

@ -0,0 +1,67 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.finder;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import com.unity3d.services.ads.gmascar.finder.GMAInitializer;
import com.unity3d.services.ads.gmascar.finder.PresenceDetector;
import com.unity3d.services.ads.gmascar.finder.ScarVersionFinder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ScarVersionFinderTest {
@Mock
MobileAdsBridge mobileAdsBridgeMock;
@Mock
PresenceDetector presenceDetector;
@Mock
GMAInitializer gmaInitializer;
@Before
public void setup() {
Mockito.when(gmaInitializer.getInitializeListenerBridge()).thenReturn(Mockito.mock(InitializeListenerBridge.class));
}
@Test
public void testScarVersionFinder() {
Mockito.when(mobileAdsBridgeMock.getVersionString()).thenReturn("afma-sdk-a-v204890999.203404000.1");
ScarVersionFinder scarVersionFinder = new ScarVersionFinder(mobileAdsBridgeMock, presenceDetector, gmaInitializer);
long versionCode = scarVersionFinder.getGoogleSdkVersionCode();
Assert.assertEquals(203404000, versionCode);
}
@Test
public void testScarVersionFinderNullVersion() {
MobileAdsBridge mobileAdsBridgeMock = Mockito.mock(MobileAdsBridge.class);
Mockito.when(mobileAdsBridgeMock.getVersionString()).thenReturn(null);
ScarVersionFinder scarVersionFinder = new ScarVersionFinder(mobileAdsBridgeMock, presenceDetector, gmaInitializer);
long versionCode = scarVersionFinder.getGoogleSdkVersionCode();
Assert.assertEquals(-1, versionCode);
}
@Test
public void testScarVersionFinderMissingVersion() {
MobileAdsBridge mobileAdsBridgeMock = Mockito.mock(MobileAdsBridge.class);
Mockito.when(mobileAdsBridgeMock.getVersionString()).thenReturn("");
ScarVersionFinder scarVersionFinder = new ScarVersionFinder(mobileAdsBridgeMock, presenceDetector, gmaInitializer);
long versionCode = scarVersionFinder.getGoogleSdkVersionCode();
Assert.assertEquals(-1, versionCode);
}
@Test
public void testScarVersionFinderInvalidVersion() {
MobileAdsBridge mobileAdsBridgeMock = Mockito.mock(MobileAdsBridge.class);
Mockito.when(mobileAdsBridgeMock.getVersionString()).thenReturn("invalid");
ScarVersionFinder scarVersionFinder = new ScarVersionFinder(mobileAdsBridgeMock, presenceDetector, gmaInitializer);
long versionCode = scarVersionFinder.getGoogleSdkVersionCode();
Assert.assertEquals(-1, versionCode);
}
}

Просмотреть файл

@ -180,6 +180,8 @@ public class UnityServices {
}
private static String createExpectedParametersString(String fieldName, Object current, Object received) {
return "\n - " + fieldName + " Current: " + current.toString() + " | Received: " + received.toString();
String currentSafeString = current == null ? "null" : current.toString();
String receivedSafeString = received == null ? "null" : received.toString();
return "\n - " + fieldName + " Current: " + currentSafeString + " | Received: " + receivedSafeString;
}
}

Просмотреть файл

@ -0,0 +1,57 @@
package com.unity3d.services.ads.api;
import com.unity3d.services.ads.gmascar.GMAScarAdapterBridge;
import com.unity3d.services.core.webview.bridge.WebViewCallback;
import com.unity3d.services.core.webview.bridge.WebViewExposed;
import org.json.JSONArray;
import org.json.JSONException;
public class GMAScar {
private static GMAScarAdapterBridge gmaScarAdapterBridge = new GMAScarAdapterBridge();
@WebViewExposed
public static void initializeScar(final WebViewCallback callback) {
gmaScarAdapterBridge.initializeScar();
callback.invoke();
}
@WebViewExposed
public static void getVersion(final WebViewCallback callback) {
gmaScarAdapterBridge.getVersion();
callback.invoke();
}
public static void isInitialized(final WebViewCallback callback) {
gmaScarAdapterBridge.isInitialized();
callback.invoke();
}
@WebViewExposed
public static void getSCARSignals(final JSONArray interstitialList, final JSONArray rewardedList, final WebViewCallback callback) throws JSONException {
gmaScarAdapterBridge.getSCARSignals(getPlacementList(interstitialList), getPlacementList(rewardedList));
callback.invoke();
}
@WebViewExposed
public static void load(final String placementId, final String queryId, final Boolean canSkip, final String adUnitId, final String adString, final Integer videoLengthMs, final WebViewCallback callback) {
gmaScarAdapterBridge.load(canSkip, placementId, queryId, adString, adUnitId, videoLengthMs);
callback.invoke();
}
@WebViewExposed
public static void show(final String placementId, final String queryId, final Boolean canSkip, final WebViewCallback callback) {
gmaScarAdapterBridge.show(placementId, queryId, canSkip);
callback.invoke();
}
private static String[] getPlacementList(JSONArray placements) throws JSONException {
String[] placementIdList = new String[placements.length()];
for (int placementIndex = 0; placementIndex < placements.length(); placementIndex++) {
placementIdList[placementIndex] = placements.getString(placementIndex);
}
return placementIdList;
}
}

Просмотреть файл

@ -4,10 +4,10 @@ import android.os.ConditionVariable;
import com.unity3d.ads.IUnityAdsListener;
import com.unity3d.ads.UnityAds;
import com.unity3d.ads.properties.AdsProperties;
import com.unity3d.services.ads.UnityAdsImplementation;
import com.unity3d.services.ads.adunit.AdUnitOpen;
import com.unity3d.services.ads.placement.Placement;
import com.unity3d.ads.properties.AdsProperties;
import com.unity3d.services.ads.token.TokenStorage;
import com.unity3d.services.core.configuration.Configuration;
import com.unity3d.services.core.log.DeviceLog;
@ -32,7 +32,8 @@ public class AdsModuleConfiguration implements IAdsModuleConfiguration {
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
com.unity3d.services.ads.api.Token.class,
com.unity3d.services.ads.api.GMAScar.class
};
return list;

Просмотреть файл

@ -0,0 +1,120 @@
package com.unity3d.services.ads.gmascar;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.services.ads.gmascar.adapters.ScarAdapterFactory;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import com.unity3d.services.ads.gmascar.finder.GMAInitializer;
import com.unity3d.services.ads.gmascar.finder.PresenceDetector;
import com.unity3d.services.ads.gmascar.finder.ScarVersionFinder;
import com.unity3d.services.ads.gmascar.handlers.ScarInterstitialAdHandler;
import com.unity3d.services.ads.gmascar.handlers.ScarRewardedAdHandler;
import com.unity3d.services.ads.gmascar.handlers.SignalsHandler;
import com.unity3d.services.ads.gmascar.handlers.WebViewErrorHandler;
import com.unity3d.services.core.properties.ClientProperties;
/**
* Adapter bridge that uses the Scar Adapter module based on the version of the GMA SDK in the developer's game.
*/
public class GMAScarAdapterBridge {
private IScarAdapter _scarAdapter;
private MobileAdsBridge _mobileAdsBridge;
private ScarVersionFinder _scarVersionFinder;
private InitializeListenerBridge _initializationListenerBridge;
private InitializationStatusBridge _initializationStatusBridge;
private AdapterStatusBridge _adapterStatusBridge;
private PresenceDetector _presenceDetector;
private GMAInitializer _gmaInitializer;
private WebViewErrorHandler _webViewErrorHandler;
private ScarAdapterFactory _scarAdapterFactory;
public GMAScarAdapterBridge() {
_mobileAdsBridge = new MobileAdsBridge();
_initializationStatusBridge = new InitializationStatusBridge();
_initializationListenerBridge = new InitializeListenerBridge();
_adapterStatusBridge = new AdapterStatusBridge();
_webViewErrorHandler = new WebViewErrorHandler();
_scarAdapterFactory = new ScarAdapterFactory();
_presenceDetector = new PresenceDetector(_mobileAdsBridge, _initializationListenerBridge, _initializationStatusBridge, _adapterStatusBridge);
_gmaInitializer = new GMAInitializer(_mobileAdsBridge, _initializationListenerBridge, _initializationStatusBridge, _adapterStatusBridge);
_scarVersionFinder = new ScarVersionFinder(_mobileAdsBridge, _presenceDetector, _gmaInitializer);
}
public void initializeScar() {
if (_presenceDetector.areGMAClassesPresent()) {
_gmaInitializer.initializeGMA();
} else {
_webViewErrorHandler.handleError(new GMAAdsError(GMAEvent.INIT_ERROR));
}
}
public boolean isInitialized() {
return _gmaInitializer.isInitialized();
}
public void getVersion() {
_scarVersionFinder.getVersion();
}
public void getSCARSignals(String[] interstitialList, String[] rewardedList) {
_scarAdapter = getScarAdapterObject();
SignalsHandler signalListener = new SignalsHandler();
if (_scarAdapter != null) {
_scarAdapter.getSCARSignals(ClientProperties.getApplicationContext(), interstitialList, rewardedList, signalListener);
} else {
_webViewErrorHandler.handleError(GMAAdsError.InternalSignalsError("Could not create SCAR adapter object"));
}
}
public void load(final boolean canSkip, final String placementId, final String queryId, final String adString, final String adUnitId, final int videoLengthMs) {
ScarAdMetadata scarAdMetadata = new ScarAdMetadata(placementId, queryId, adUnitId, adString, videoLengthMs);
_scarAdapter = getScarAdapterObject();
if (_scarAdapter != null) {
if (canSkip) {
loadInterstitialAd(scarAdMetadata);
} else {
loadRewardedAd(scarAdMetadata);
}
} else {
_webViewErrorHandler.handleError(GMAAdsError.InternalLoadError(scarAdMetadata, "Scar Adapter object is null"));
}
}
private void loadInterstitialAd(final ScarAdMetadata scarAdMetadata) {
ScarInterstitialAdHandler adListener = new ScarInterstitialAdHandler(scarAdMetadata);
_scarAdapter.loadInterstitialAd(ClientProperties.getApplicationContext(), scarAdMetadata, adListener);
}
private void loadRewardedAd(final ScarAdMetadata scarAdMetadata) {
ScarRewardedAdHandler adListener = new ScarRewardedAdHandler(scarAdMetadata);
_scarAdapter.loadRewardedAd(ClientProperties.getApplicationContext(), scarAdMetadata, adListener);
}
public void show(final String placementId, final String queryId, final boolean canSkip) {
ScarAdMetadata scarAdMetadata = new ScarAdMetadata(placementId, queryId);
_scarAdapter = getScarAdapterObject();
if (_scarAdapter != null) {
_scarAdapter.show(ClientProperties.getActivity(), queryId, placementId);
} else {
_webViewErrorHandler.handleError(GMAAdsError.InternalShowError(scarAdMetadata, "Scar Adapter object is null"));
}
}
private IScarAdapter getScarAdapterObject() {
if (_scarAdapter == null) {
long minorVersion = _scarVersionFinder.getGoogleSdkVersionCode();
_scarAdapter = _scarAdapterFactory.createScarAdapter(minorVersion, _webViewErrorHandler);
}
return _scarAdapter;
}
}

Просмотреть файл

@ -0,0 +1,26 @@
package com.unity3d.services.ads.gmascar.adapters;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.services.core.log.DeviceLog;
public class ScarAdapterFactory {
public static final int CODE_20_0 = 210402000;
public static final int CODE_19_7 = 204204000;
public static final int CODE_19_5 = 203404000;
public static final int CODE_19_2 = 201604000;
public IScarAdapter createScarAdapter(long gmaVersionCode, IAdsErrorHandler adsErrorHandler) {
IScarAdapter scarAdapter = null;
if (gmaVersionCode >= CODE_20_0) {
scarAdapter = new com.unity3d.scar.adapter.v2000.ScarAdapter(adsErrorHandler);
} else if (gmaVersionCode >= CODE_19_5 && gmaVersionCode <= CODE_19_7) {
scarAdapter = new com.unity3d.scar.adapter.v1950.ScarAdapter(adsErrorHandler);
} else if (gmaVersionCode >= CODE_19_2) {
scarAdapter = new com.unity3d.scar.adapter.v1920.ScarAdapter(adsErrorHandler);
} else {
DeviceLog.debug("SCAR version %s is not supported.", gmaVersionCode);
}
return scarAdapter;
}
}

Просмотреть файл

@ -0,0 +1,43 @@
package com.unity3d.services.ads.gmascar.bridges;
import com.unity3d.services.core.log.DeviceLog;
import java.util.HashMap;
public class AdapterStatusBridge extends GenericBridge {
private static final String initializeStateMethodName = "getInitializationState";
// This class contains the AdapterState Enum
private Class _adapterStateClass;
public AdapterStatusBridge() {
super(new HashMap<String, Class[]>() {{
put(initializeStateMethodName, new Class[]{});
}});
AdapterStatusStateBridge adapterStatusStateBridge = new AdapterStatusStateBridge();
try {
_adapterStateClass = Class.forName(adapterStatusStateBridge.getClassName());
} catch (ClassNotFoundException e) {
DeviceLog.debug("ERROR: Could not find class %s %s", adapterStatusStateBridge.getClassName(), e.getLocalizedMessage());
}
}
protected String getClassName() {
return "com.google.android.gms.ads.initialization.AdapterStatus";
}
public boolean isGMAInitialized(Object adapterState) {
Object[] states = getAdapterStatesEnum();
if (states == null) {
DeviceLog.debug("ERROR: Could not get adapter states enum from AdapterStatus.State");
return false;
}
// States[0]: AdapterState.NOT_READY | States[1]: AdapterState.READY
return (callNonVoidMethod(initializeStateMethodName, adapterState, new Object[]{}) == states[1]);
}
public Object[] getAdapterStatesEnum() {
return _adapterStateClass.getEnumConstants();
}
}

Просмотреть файл

@ -0,0 +1,11 @@
package com.unity3d.services.ads.gmascar.bridges;
public class AdapterStatusStateBridge {
public AdapterStatusStateBridge() {};
public String getClassName() {
return "com.google.android.gms.ads.initialization.AdapterStatus$State";
}
}

Просмотреть файл

@ -0,0 +1,126 @@
package com.unity3d.services.ads.gmascar.bridges;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.services.core.log.DeviceLog;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public abstract class GenericBridge {
private String _className;
private Map<String, Class[]> _functionAndParameters;
private Map<String, Method> _methodMap;
private boolean _methodMapBuilt = false;
protected abstract String getClassName();
public GenericBridge(Map<String, Class[]> functionAndParameters) {
_className = getClassName();
_functionAndParameters = functionAndParameters;
_methodMap = new HashMap<>();
buildMethodMap();
}
public Map<String,Class[]> getFunctionMap() {
return _functionAndParameters;
}
public Class classForName() {
try {
Class getClass = Class.forName(_className);
if (getClass != null) {
return getClass;
} else {
return null;
}
} catch (ClassNotFoundException e) {
DeviceLog.debug("ERROR: Could not find GMA SDK Class %s %s", _className, e.getLocalizedMessage());
return null;
}
}
public boolean exists() {
if (classForName() == null) {
DeviceLog.debug("ERROR: Could not find class %s", _className);
return false;
}
if (!_methodMapBuilt) {
buildMethodMap();
}
if (_methodMap.size() != getFunctionMap().size()) {
return false;
}
return true;
}
private void buildMethodMap() {
boolean methodMapNoErrors = true;
for (Map.Entry<String, Class[]> entry : getFunctionMap().entrySet()) {
Class[] parameterClasses = entry.getValue();
try {
Method method = getReflectiveMethod(classForName(), entry.getKey(), parameterClasses);
if (method != null) {
_methodMap.put(entry.getKey(), method);
}
} catch (Exception e) {
DeviceLog.debug("ERROR: Could not find %s class with method %s and parameters : %s", _className, entry.getKey(), parameterClasses);
methodMapNoErrors = false;
}
}
_methodMapBuilt = methodMapNoErrors;
}
private Method getMethod(String methodName) {
return _methodMap.get(methodName);
}
private Method getReflectiveMethod(Class methodClass, String methodName, Class... parameterClasses) {
Method method = null;
try {
method = methodClass.getDeclaredMethod(methodName, parameterClasses);
} catch (Exception e) {
DeviceLog.debug("ERROR: Could not find method %s in %s", methodName, methodClass.getName() +
" " + e.getLocalizedMessage());
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.METHOD_ERROR);
} finally {
return method;
}
}
public void callVoidMethod(String methodName, Object callingObj, Object... parameters) {
Method method = getMethod(methodName);
if (method == null) {
DeviceLog.debug("ERROR: Could not find method %s", methodName);
return;
}
try {
method.invoke(callingObj, parameters);
} catch (Exception e) {
DeviceLog.debug("ERROR: Could not invoke method %s : %s", methodName, e.getLocalizedMessage());
}
}
public Object callNonVoidMethod(String methodName, Object callingObj, Object... parameters) {
Method method = getMethod(methodName);
if (method == null) {
DeviceLog.debug("ERROR: Could not find method %s", methodName);
return null;
}
try {
return method.invoke(callingObj, parameters);
} catch (Exception e) {
DeviceLog.debug("ERROR: Could not invoke method %s : %s", methodName, e.getLocalizedMessage());
}
return null;
}
}

Просмотреть файл

@ -0,0 +1,23 @@
package com.unity3d.services.ads.gmascar.bridges;
import java.util.HashMap;
import java.util.Map;
public class InitializationStatusBridge extends GenericBridge {
private static final String adapterStatusMapMethodName = "getAdapterStatusMap";
public InitializationStatusBridge() {
super(new HashMap<String, Class[]>() {{
put(adapterStatusMapMethodName, new Class[]{});
}});
}
public String getClassName() {
return "com.google.android.gms.ads.initialization.InitializationStatus";
}
public Map<String, Object> getAdapterStatusMap(Object initStatusObj) {
return (Map<String, Object>) callNonVoidMethod(adapterStatusMapMethodName,
initStatusObj, new Object[]{});
}
}

Просмотреть файл

@ -0,0 +1,55 @@
package com.unity3d.services.ads.gmascar.bridges;
import com.unity3d.services.ads.gmascar.listeners.IInitializationStatusListener;
import com.unity3d.services.core.log.DeviceLog;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
public class InitializeListenerBridge extends GenericBridge {
private static final String initializationCompleteMethodName = "onInitializationComplete";
private IInitializationStatusListener _initializationStatusListener;
public InitializeListenerBridge() {
super(new HashMap<String, Class[]>(){{
try {
put(initializationCompleteMethodName, new Class[]{Class.forName("com.google.android.gms.ads.initialization.InitializationStatus")});
} catch (ClassNotFoundException e) {
DeviceLog.debug("Could not find class \"com.google.android.gms.ads.initialization.InitializationStatus\" %s", e.getLocalizedMessage());
}
}});
}
public String getClassName() {
return "com.google.android.gms.ads.initialization.OnInitializationCompleteListener";
}
public void setStatusListener(IInitializationStatusListener initializationStatusListener) {
_initializationStatusListener = initializationStatusListener;
}
public Object createInitializeListenerProxy() {
try {
Object initProxy = Proxy.newProxyInstance(classForName().getClassLoader(),
new Class<?>[]{classForName()}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
if (method.getName().equals(initializationCompleteMethodName)) {
if (_initializationStatusListener != null) {
// args[0] contains an InitializationStatus object
_initializationStatusListener.onInitializationComplete(args[0]);
}
}
return null;
}
});
return initProxy;
} catch (Exception e) {
DeviceLog.debug("ERROR: Could not create InitializeCompletionListener");
}
return null;
}
}

Просмотреть файл

@ -0,0 +1,46 @@
package com.unity3d.services.ads.gmascar.bridges;
import android.content.Context;
import com.unity3d.services.core.log.DeviceLog;
import java.util.HashMap;
public class MobileAdsBridge extends GenericBridge {
private static final String initializeMethodName = "initialize";
private static final String initializationStatusMethodName = "getInitializationStatus";
private static final String versionStringMethodName = "getVersionString";
public MobileAdsBridge() {
super(new HashMap<String, Class[]>() {{
try {
put(initializeMethodName, new Class[]{Context.class, Class.forName("com.google.android.gms.ads.initialization.OnInitializationCompleteListener")});
} catch (ClassNotFoundException e) {
DeviceLog.debug("Could not find class \"com.google.android.gms.ads.initialization.OnInitializationCompleteListener\" %s", e.getLocalizedMessage());
}
put(initializationStatusMethodName, new Class[]{});
put(versionStringMethodName, new Class[]{});
}});
}
public String getClassName() {
return "com.google.android.gms.ads.MobileAds";
}
public void initialize(Context context, Object initializeListener) {
callVoidMethod(initializeMethodName, null, new Object[]{context, initializeListener});
}
public String getVersionString() {
Object versionString = callNonVoidMethod(versionStringMethodName, null, new Object[]{});
if (versionString == null) {
return "0.0.0";
}
return versionString.toString();
}
public Object getInitializationStatus () {
return callNonVoidMethod(initializationStatusMethodName, null, new Object[]{});
}
}

Просмотреть файл

@ -0,0 +1,71 @@
package com.unity3d.services.ads.gmascar.finder;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import com.unity3d.services.core.log.DeviceLog;
import com.unity3d.services.core.properties.ClientProperties;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
import java.util.Map;
public class GMAInitializer {
private MobileAdsBridge _mobileAdsBridge;
private InitializeListenerBridge _initializationListenerBridge;
private InitializationStatusBridge _initializationStatusBridge;
private AdapterStatusBridge _adapterStatusBridge;
public GMAInitializer(MobileAdsBridge mobileAdsBridge, InitializeListenerBridge initializeListenerBridge,
InitializationStatusBridge initializationStatusBridge, AdapterStatusBridge adapterStatusBridge) {
_mobileAdsBridge = mobileAdsBridge;
_initializationListenerBridge = initializeListenerBridge;
_initializationStatusBridge = initializationStatusBridge;
_adapterStatusBridge = adapterStatusBridge;
}
// We need to initialize GMA SDK in order to get the version string
public void initializeGMA() {
if (isInitialized()) {
return;
} else {
_mobileAdsBridge.initialize(ClientProperties.getApplicationContext(), _initializationListenerBridge.createInitializeListenerProxy());
}
}
public boolean initSuccessful(Object initStatus) {
Map<String, Object> statusMap = _initializationStatusBridge.getAdapterStatusMap(initStatus);
Object adapterState = statusMap.get(_mobileAdsBridge.getClassName());
if (adapterState != null) {
if (_adapterStatusBridge.isGMAInitialized(adapterState)) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.INIT_SUCCESS);
return true;
} else {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.INIT_ERROR);
return false;
}
}
return false;
}
public boolean isInitialized() {
boolean isInitialized = false;
try {
isInitialized = initSuccessful(_mobileAdsBridge.getInitializationStatus());
} catch (Exception e) {
isInitialized = false;
DeviceLog.debug("ERROR: Could not get initialization status of GMA SDK - %s", e.getLocalizedMessage());
} finally {
return isInitialized;
}
}
public InitializeListenerBridge getInitializeListenerBridge() {
return _initializationListenerBridge;
}
}

Просмотреть файл

@ -0,0 +1,31 @@
package com.unity3d.services.ads.gmascar.finder;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializeListenerBridge;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
public class PresenceDetector {
private MobileAdsBridge _mobileAdsBridge;
private InitializeListenerBridge _initializationListenerBridge;
private InitializationStatusBridge _initializationStatusBridge;
private AdapterStatusBridge _adapterStatusBridge;
public PresenceDetector(MobileAdsBridge mobileAdsBridge, InitializeListenerBridge initializeListenerBridge,
InitializationStatusBridge initializationStatusBridge, AdapterStatusBridge adapterStatusBridge) {
_mobileAdsBridge = mobileAdsBridge;
_initializationListenerBridge = initializeListenerBridge;
_initializationStatusBridge = initializationStatusBridge;
_adapterStatusBridge = adapterStatusBridge;
}
public boolean areGMAClassesPresent() {
if (_mobileAdsBridge == null || _initializationListenerBridge == null ||
_initializationStatusBridge == null || _adapterStatusBridge == null) {
return false;
}
return _mobileAdsBridge.exists() && _initializationListenerBridge.exists() && _initializationStatusBridge.exists()
&& _adapterStatusBridge.exists();
}
}

Просмотреть файл

@ -0,0 +1,67 @@
package com.unity3d.services.ads.gmascar.finder;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.services.ads.gmascar.bridges.MobileAdsBridge;
import com.unity3d.services.ads.gmascar.listeners.IInitializationStatusListener;
import com.unity3d.services.core.log.DeviceLog;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
public class ScarVersionFinder implements IInitializationStatusListener {
private static MobileAdsBridge _mobileAdsBridge;
private PresenceDetector _presenceDetector;
private GMAInitializer _gmaInitializer;
private long _gmaSdkVersionCode = -1;
public ScarVersionFinder(MobileAdsBridge mobileAdsBridge, PresenceDetector presenceDetector, GMAInitializer gmaInitializer) {
_mobileAdsBridge = mobileAdsBridge;
_presenceDetector = presenceDetector;
_gmaInitializer = gmaInitializer;
_gmaInitializer.getInitializeListenerBridge().setStatusListener(this);
}
public void getVersion() {
try {
if (!_presenceDetector.areGMAClassesPresent()) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.INIT_GMA, GMAEvent.VERSION, "0.0.0");
return;
}
if (!_gmaInitializer.isInitialized()) {
_gmaInitializer.initializeGMA();
} else {
findAndSendVersion(true);
}
} catch (Exception e) {
DeviceLog.debug("Got exception finding GMA SDK: %s", e.getLocalizedMessage());
}
}
public void findAndSendVersion(boolean initializeSuccess) {
String version = initializeSuccess ? _mobileAdsBridge.getVersionString() : "0.0.0";
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.INIT_GMA, GMAEvent.VERSION, version);
}
public long getGoogleSdkVersionCode() {
if (_gmaSdkVersionCode == -1) {
String gmaSdkVersion = _mobileAdsBridge.getVersionString();
if (gmaSdkVersion != null) {
String[] versionComponents = gmaSdkVersion.split("\\.");
if (versionComponents.length > 1) {
_gmaSdkVersionCode = Long.parseLong(versionComponents[1]);
}
}
}
return _gmaSdkVersionCode;
}
@Override
// @param initStatus InitializationStatus Object retrieved through reflection
public void onInitializationComplete(Object initStatus) {
boolean isInitSuccessful = _gmaInitializer.initSuccessful(initStatus);
findAndSendVersion(isInitSuccessful);
}
}

Просмотреть файл

@ -0,0 +1,82 @@
package com.unity3d.services.ads.gmascar.handlers;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
import java.util.Timer;
import java.util.TimerTask;
public class ScarInterstitialAdHandler implements IScarInterstitialAdListenerWrapper {
private ScarAdMetadata _scarAdMetadata;
private boolean _finishedPlaying = false;
private boolean _hasSentStartEvents = false;
private Timer _playbackTimer;
private TimerTask _playbackTimerTask = new TimerTask() {
@Override
public void run() {
_finishedPlaying = true;
}
};
public ScarInterstitialAdHandler(ScarAdMetadata scarAdMetadata) {
_scarAdMetadata = scarAdMetadata;
_playbackTimer = new Timer();
}
@Override
public void onAdLoaded() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_LOADED, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId());
}
@Override
public void onAdFailedToLoad(int errorCode, String errorString) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.LOAD_ERROR, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId(), errorString, errorCode);
}
@Override
public void onAdOpened() {
// We send all three events back to back since we don't have quartile callbacks from GMA
if (!_hasSentStartEvents) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_STARTED);
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.FIRST_QUARTILE);
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.MIDPOINT);
_hasSentStartEvents = true;
}
// Start timer here to see if the user watched to completion
_finishedPlaying = false;
_playbackTimer.schedule(_playbackTimerTask, _scarAdMetadata.getVideoLengthMs());
}
@Override
public void onAdFailedToShow(int errorCode, String errorString) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.INTERSTITIAL_SHOW_ERROR, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId(), errorString, errorCode);
}
@Override
public void onAdClicked() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_CLICKED);
}
@Override
public void onAdClosed() {
if (!_finishedPlaying) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_SKIPPED);
_playbackTimer.cancel();
}
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_CLOSED);
}
@Override
public void onAdLeftApplication() {
}
@Override
public void onAdImpression() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.INTERSTITIAL_IMPRESSION_RECORDED, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId());
}
}

Просмотреть файл

@ -0,0 +1,85 @@
package com.unity3d.services.ads.gmascar.handlers;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
import java.util.Timer;
import java.util.TimerTask;
public class ScarRewardedAdHandler implements IScarRewardedAdListenerWrapper {
private ScarAdMetadata _scarAdMetadata;
private boolean _finishedPlaying = false;
private boolean _hasRewarded = false;
private boolean _hasSentStartEvents = false;
private Timer _playbackTimer;
private TimerTask _playbackTimerTask = new TimerTask() {
@Override
public void run() {
_finishedPlaying = true;
}
};
public ScarRewardedAdHandler(ScarAdMetadata scarAdMetadata) {
_scarAdMetadata = scarAdMetadata;
_playbackTimer = new Timer();
}
@Override
public void onRewardedAdLoaded() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_LOADED, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId());
}
@Override
public void onRewardedAdFailedToLoad(int errorCode, String errorString) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.LOAD_ERROR, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId(), errorString, errorCode);
}
@Override
public void onRewardedAdOpened() {
// We send all three events back to back since we don't have quartile callbacks from GMA
if (!_hasSentStartEvents) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_STARTED);
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.FIRST_QUARTILE);
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.MIDPOINT);
_hasSentStartEvents = true;
}
if (!_hasRewarded) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_EARNED_REWARD);
_hasRewarded = true;
}
_finishedPlaying = false;
_playbackTimer.schedule(_playbackTimerTask, _scarAdMetadata.getVideoLengthMs());
}
@Override
public void onRewardedAdFailedToShow(int errorCode, String errorString) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.REWARDED_SHOW_ERROR, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId(), errorString, errorCode);
}
@Override
public void onUserEarnedReward() {
if (!_hasRewarded) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_EARNED_REWARD);
_hasRewarded = true;
}
}
@Override
public void onRewardedAdClosed() {
if (!_finishedPlaying) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_SKIPPED);
_playbackTimer.cancel();
}
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.AD_CLOSED);
}
@Override
public void onAdImpression() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.REWARDED_IMPRESSION_RECORDED, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId());
}
}

Просмотреть файл

@ -0,0 +1,19 @@
package com.unity3d.services.ads.gmascar.handlers;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
public class SignalsHandler implements ISignalCollectionListener {
@Override
public void onSignalsCollected(String signalsMap) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.SIGNALS, signalsMap);
}
@Override
public void onSignalsCollectionFailed(String errorMsg) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.GMA, GMAEvent.SIGNALS_ERROR, errorMsg);
}
}

Просмотреть файл

@ -0,0 +1,15 @@
package com.unity3d.services.ads.gmascar.handlers;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.WebViewAdsError;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
public class WebViewErrorHandler implements IAdsErrorHandler<WebViewAdsError> {
@Override
public void handleError(WebViewAdsError webViewAdsError) {
WebViewEventCategory category = WebViewEventCategory.valueOf(webViewAdsError.getDomain());
WebViewApp.getCurrentApp().sendEvent(category, webViewAdsError.getErrorCategory(), webViewAdsError.getErrorArguments());
}
}

Просмотреть файл

@ -0,0 +1,5 @@
package com.unity3d.services.ads.gmascar.listeners;
public interface IInitializationStatusListener {
void onInitializationComplete(Object initStatus);
}

Просмотреть файл

@ -19,5 +19,7 @@ public enum WebViewEventCategory {
PERMISSIONS,
STORE,
LOAD_API,
TOKEN
TOKEN,
INIT_GMA,
GMA
}

1
unity-scaradapter-1920/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/build

Просмотреть файл

@ -0,0 +1,63 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
testCoverageEnabled true
}
}
}
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation "org.mockito:mockito-android:2.25.0"
androidTestImplementation 'com.google.android.gms:play-services-ads:19.2.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation 'com.google.android.gms:play-services-ads:19.2.0'
api project(':unity-scaradapter-common')
compileOnly 'com.google.android.gms:play-services-ads:19.2.0'
}
task deleteOldJar(type: Delete) {
delete("../unity-ads/libs/${project.name}.jar")
}
task copyJars(type: Copy) {
from('build/intermediates/compile_library_classes_jar/release/')
into('../unity-ads/libs/')
include('classes.jar')
rename('classes.jar', "${project.name}.jar")
}
copyJars.dependsOn(deleteOldJar)
project.tasks.whenTaskAdded { Task theTask ->
if (theTask.name == 'bundleLibCompileToJarRelease') {
theTask.finalizedBy(copyJars)
}
}

Просмотреть файл

21
unity-scaradapter-1920/proguard-rules.pro поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Просмотреть файл

@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.scar.adapter.v1920">>
<application>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"
/>
</application>
</manifest>

Просмотреть файл

@ -0,0 +1,13 @@
package com.unity3d.ads.test;
import com.unity3d.scar.adapter.v1920.signals.SignalsReaderTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
SignalsReaderTest.class
})
public class InstrumentationTestSuite {
}

Просмотреть файл

@ -0,0 +1,48 @@
package com.unity3d.scar.adapter.v1920.signals;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Matchers.any;
@RunWith(MockitoJUnitRunner.class)
public class SignalsReaderTest {
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
private ISignalCollectionListener _signalCollectionListener;
@Before
public void before() {
_signalCollectionListener = Mockito.mock(ISignalCollectionListener.class);
}
@Test
public void testGetScarSignals() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{"video"}, new String[]{"rewarded"}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
@Test
public void testGetScarSignalsNoRewarded() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{"video"}, new String[]{}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
@Test
public void testGetScarSignalsNoInterstitial() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{}, new String[]{"rewarded"}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.scar.adapter.v1920">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Просмотреть файл

@ -0,0 +1,59 @@
package com.unity3d.scar.adapter.v1920;
import android.content.Context;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.ScarAdapterBase;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1920.scarads.ScarInterstitialAd;
import com.unity3d.scar.adapter.v1920.scarads.ScarRewardedAd;
import com.unity3d.scar.adapter.v1920.signals.SignalsReader;
import com.unity3d.scar.adapter.v1920.signals.SignalsStorage;
import static com.unity3d.scar.adapter.common.Utils.runOnUiThread;
public class ScarAdapter extends ScarAdapterBase implements IScarAdapter {
private SignalsStorage _scarSignalStorage;
public ScarAdapter(IAdsErrorHandler adsErrorHandler) {
super(adsErrorHandler);
_scarSignalStorage = new SignalsStorage();
_scarSignalReader = new SignalsReader(_scarSignalStorage);
}
public void loadInterstitialAd(Context context, final ScarAdMetadata scarAd, final IScarInterstitialAdListenerWrapper adListenerWrapper) {
final ScarInterstitialAd interstitialAd = new ScarInterstitialAd(context, _scarSignalStorage.getQueryInfoMetadata(scarAd.getPlacementId()), scarAd, _adsErrorHandler, adListenerWrapper);
runOnUiThread(new Runnable() {
@Override
public void run() {
interstitialAd.loadAd(new IScarLoadListener() {
@Override
public void onAdLoaded() {
_loadedAds.put(scarAd.getPlacementId(), interstitialAd);
}
});
}
});
}
public void loadRewardedAd(Context context, final ScarAdMetadata scarAd, final IScarRewardedAdListenerWrapper adListenerWrapper) {
final ScarRewardedAd rewardedAd = new ScarRewardedAd(context, _scarSignalStorage.getQueryInfoMetadata(scarAd.getPlacementId()), scarAd, _adsErrorHandler, adListenerWrapper);
runOnUiThread(new Runnable() {
@Override
public void run() {
rewardedAd.loadAd(new IScarLoadListener() {
@Override
public void onAdLoaded() {
_loadedAds.put(scarAd.getPlacementId(), rewardedAd);
}
});
}
});
}
}

Просмотреть файл

@ -0,0 +1,40 @@
package com.unity3d.scar.adapter.v1920.scarads;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.query.AdInfo;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.scarads.IScarAd;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1920.signals.QueryInfoMetadata;
public abstract class ScarAdBase implements IScarAd {
protected Context _context;
protected ScarAdMetadata _scarAdMetadata;
protected QueryInfoMetadata _queryInfoMetadata;
protected IAdsErrorHandler _adsErrorHandler;
public ScarAdBase(Context context, ScarAdMetadata scarAdMetadata, QueryInfoMetadata queryInfoMetadata, IAdsErrorHandler adsErrorHandler) {
_context = context;
_scarAdMetadata = scarAdMetadata;
_queryInfoMetadata = queryInfoMetadata;
_adsErrorHandler = adsErrorHandler;
}
@Override
public void loadAd(IScarLoadListener loadListener) {
if (_queryInfoMetadata != null) {
AdInfo adInfo = new AdInfo(_queryInfoMetadata.getQueryInfo(), _scarAdMetadata.getAdString());
AdRequest adRequest = new AdRequest.Builder().setAdInfo(adInfo).build();
loadAdInternal(loadListener, adRequest);
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalLoadError(_scarAdMetadata));
}
}
protected abstract void loadAdInternal(IScarLoadListener loadListener, AdRequest adRequest);
}

Просмотреть файл

@ -0,0 +1,42 @@
package com.unity3d.scar.adapter.v1920.scarads;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1920.signals.QueryInfoMetadata;
public class ScarInterstitialAd extends ScarAdBase {
private InterstitialAd _interstitialAd;
private ScarInterstitialAdListener _interstitialAdDelegate;
public ScarInterstitialAd(Context context, QueryInfoMetadata queryInfoMetadata, ScarAdMetadata scarAdMetadata, IAdsErrorHandler adsErrorHandler, IScarInterstitialAdListenerWrapper adListener) {
super(context, scarAdMetadata, queryInfoMetadata, adsErrorHandler);
_interstitialAd = new InterstitialAd(_context);
_interstitialAd.setAdUnitId(_scarAdMetadata.getAdUnitId());
_interstitialAdDelegate = new ScarInterstitialAdListener(_interstitialAd, adListener);
}
@Override
public void loadAdInternal(IScarLoadListener loadListener, AdRequest adRequest) {
_interstitialAd.setAdListener(_interstitialAdDelegate.getAdListener());
_interstitialAdDelegate.setLoadListener(loadListener);
_interstitialAd.loadAd(adRequest);
}
@Override
public void show(Activity activity) {
if (_interstitialAd.isLoaded()) {
_interstitialAd.show();
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalShowError(_scarAdMetadata));
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
package com.unity3d.scar.adapter.v1920.scarads;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.InterstitialAd;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
public class ScarInterstitialAdListener {
private InterstitialAd _interstitialAd;
private IScarInterstitialAdListenerWrapper _adListenerWrapper;
private IScarLoadListener _loadListener;
public ScarInterstitialAdListener(InterstitialAd interstitialAd, IScarInterstitialAdListenerWrapper adListenerWrapper) {
_interstitialAd = interstitialAd;
_adListenerWrapper = adListenerWrapper;
}
private AdListener _adListener = new AdListener() {
@Override
public void onAdLoaded() {
_adListenerWrapper.onAdLoaded();
if (_loadListener != null) {
_loadListener.onAdLoaded();
}
}
@Override
public void onAdFailedToLoad(int adErrorCode) {
_adListenerWrapper.onAdFailedToLoad(adErrorCode, "SCAR ad failed to load");
}
@Override
public void onAdOpened() {
_adListenerWrapper.onAdOpened();
}
@Override
public void onAdClicked() {
_adListenerWrapper.onAdClicked();
}
@Override
public void onAdLeftApplication() {
_adListenerWrapper.onAdLeftApplication();
}
@Override
public void onAdClosed() {
_adListenerWrapper.onAdClosed();
}
};
public AdListener getAdListener() {
return _adListener;
}
public void setLoadListener(IScarLoadListener loadListener) {
_loadListener = loadListener;
}
}

Просмотреть файл

@ -0,0 +1,41 @@
package com.unity3d.scar.adapter.v1920.scarads;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1920.signals.QueryInfoMetadata;
public class ScarRewardedAd extends ScarAdBase {
private RewardedAd _rewardedAd;
private ScarRewardedAdListener _rewardedAdDelegate;
public ScarRewardedAd(Context context, QueryInfoMetadata queryInfoMetadata, ScarAdMetadata scarAdMetadata, IAdsErrorHandler adsErrorHandler, IScarRewardedAdListenerWrapper adListener) {
super(context, scarAdMetadata, queryInfoMetadata, adsErrorHandler);
_rewardedAd = new RewardedAd(_context, _scarAdMetadata.getAdUnitId());
_rewardedAdDelegate = new ScarRewardedAdListener(_rewardedAd, adListener);
}
@Override
public void loadAdInternal(IScarLoadListener loadListener, AdRequest adRequest) {
_rewardedAdDelegate.setLoadListener(loadListener);
_rewardedAd.loadAd(adRequest, _rewardedAdDelegate.getRewardedAdLoadCallback());
}
@Override
public void show(Activity activity) {
if (_rewardedAd.isLoaded()) {
_rewardedAd.show(activity, _rewardedAdDelegate.getRewardedAdCallback());
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalShowError(_scarAdMetadata));
}
}
}

Просмотреть файл

@ -0,0 +1,68 @@
package com.unity3d.scar.adapter.v1920.scarads;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.google.android.gms.ads.rewarded.RewardItem;
import com.google.android.gms.ads.rewarded.RewardedAdCallback;
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
public class ScarRewardedAdListener {
private RewardedAd _rewardedAd;
private IScarRewardedAdListenerWrapper _adListenerWrapper;
private IScarLoadListener _loadListener;
public ScarRewardedAdListener(RewardedAd rewardedAd, IScarRewardedAdListenerWrapper adListenerWrapper) {
_rewardedAd = rewardedAd;
_adListenerWrapper = adListenerWrapper;
}
private RewardedAdLoadCallback _rewardedAdLoadCallback = new RewardedAdLoadCallback() {
@Override
public void onRewardedAdLoaded() {
_adListenerWrapper.onRewardedAdLoaded();
if (_loadListener != null) {
_loadListener.onAdLoaded();
}
}
@Override
public void onRewardedAdFailedToLoad(int adErrorCode) {
_adListenerWrapper.onRewardedAdFailedToLoad(adErrorCode, "SCAR ad failed to show");
}
};
private RewardedAdCallback rewardedAdCallback = new RewardedAdCallback () {
@Override
public void onRewardedAdOpened() {
_adListenerWrapper.onRewardedAdOpened();
}
@Override
public void onRewardedAdFailedToShow(int adErrorCode) {
_adListenerWrapper.onRewardedAdFailedToShow(adErrorCode, "SCAR ad failed to show");
}
@Override
public void onUserEarnedReward(RewardItem rewardItem) {
_adListenerWrapper.onUserEarnedReward();
}
@Override
public void onRewardedAdClosed() {
_adListenerWrapper.onRewardedAdClosed();
}
};
public RewardedAdCallback getRewardedAdCallback() {
return rewardedAdCallback;
}
public RewardedAdLoadCallback getRewardedAdLoadCallback() { return _rewardedAdLoadCallback; }
public void setLoadListener(IScarLoadListener loadListener) {
_loadListener = loadListener;
}
}

Просмотреть файл

@ -0,0 +1,33 @@
package com.unity3d.scar.adapter.v1920.signals;
import android.util.Log;
import com.google.android.gms.ads.query.QueryInfo;
import com.google.android.gms.ads.query.QueryInfoGenerationCallback;
import com.unity3d.scar.adapter.common.DispatchGroup;
import com.unity3d.scar.adapter.v1920.ScarAdapter;
public class QueryInfoCallback extends QueryInfoGenerationCallback {
private DispatchGroup _dispatchGroup;
private QueryInfoMetadata _gmaQueryInfoMetadata;
public QueryInfoCallback(final QueryInfoMetadata gmaQueryInfoMetadata, final DispatchGroup dispatchGroup) {
_dispatchGroup = dispatchGroup;
_gmaQueryInfoMetadata = gmaQueryInfoMetadata;
}
// Called when QueryInfo generation succeeds
@Override
public void onSuccess(final QueryInfo queryInfo) {
_gmaQueryInfoMetadata.setQueryInfo(queryInfo);
_dispatchGroup.leave();
}
// Called when QueryInfo generation fails
@Override
public void onFailure(String failureMsg) {
_gmaQueryInfoMetadata.setError(failureMsg);
_dispatchGroup.leave();
}
}

Просмотреть файл

@ -0,0 +1,41 @@
package com.unity3d.scar.adapter.v1920.signals;
import com.google.android.gms.ads.query.QueryInfo;
public class QueryInfoMetadata {
private String _placementId;
private QueryInfo _queryInfo;
private String _error;
public QueryInfoMetadata(String placementId) {
_placementId = placementId;
}
public String getPlacementId() {
return _placementId;
}
public QueryInfo getQueryInfo() {
return _queryInfo;
}
public String getQueryStr() {
String query = null;
if (_queryInfo != null) {
query = _queryInfo.getQuery();
}
return query;
}
public String getError() {
return _error;
}
public void setQueryInfo(QueryInfo queryInfo) {
_queryInfo = queryInfo;
}
public void setError(String error) {
_error = error;
}
}

Просмотреть файл

@ -0,0 +1,85 @@
package com.unity3d.scar.adapter.v1920.signals;
import android.content.Context;
import com.google.android.gms.ads.AdFormat;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.query.QueryInfo;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import com.unity3d.scar.adapter.common.DispatchGroup;
import com.unity3d.scar.adapter.common.signals.ISignalsReader;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class SignalsReader implements ISignalsReader {
private static SignalsStorage _signalsStorage;
public SignalsReader(SignalsStorage signalsStorage) {
_signalsStorage = signalsStorage;
}
@Override
public void getSCARSignals(Context context, String[] interstitialList, String[] rewardedList,
ISignalCollectionListener signalCompletionListener) {
DispatchGroup dispatchGroup = new DispatchGroup();
for (String interstitialId : interstitialList) {
dispatchGroup.enter();
getSCARSignal(context, interstitialId, AdFormat.INTERSTITIAL, dispatchGroup);
}
for (String rewardedId : rewardedList) {
dispatchGroup.enter();
getSCARSignal(context, rewardedId, AdFormat.REWARDED, dispatchGroup);
}
dispatchGroup.notify(new GMAScarDispatchCompleted(signalCompletionListener));
}
private void getSCARSignal(Context context, String placementId, AdFormat adType, DispatchGroup dispatchGroup) {
AdRequest request = new AdRequest.Builder().build();
QueryInfoMetadata gmaQueryInfoMetadata = new QueryInfoMetadata(placementId);
QueryInfoCallback gmaQueryInfoCallback = new QueryInfoCallback(gmaQueryInfoMetadata, dispatchGroup);
// Callback on the QueryInfoCallback is async here.
_signalsStorage.put(placementId, gmaQueryInfoMetadata);
QueryInfo.generate(context, adType, request, gmaQueryInfoCallback);
}
private class GMAScarDispatchCompleted implements Runnable {
private ISignalCollectionListener _signalListener;
public GMAScarDispatchCompleted(ISignalCollectionListener signalListener) {
_signalListener = signalListener;
}
@Override
public void run() {
// Called once every dispatched thread has returned.
// Build up the JSON response since we received all the signals.
Map<String, String> _placementSignalMap = new HashMap<>();
String errorMessage = null;
for(Map.Entry<String, QueryInfoMetadata> queryInfoMetadata : _signalsStorage.getPlacementQueryInfoMap().entrySet()) {
QueryInfoMetadata currentQueryMetadata = queryInfoMetadata.getValue();
_placementSignalMap.put(currentQueryMetadata.getPlacementId(), currentQueryMetadata.getQueryStr());
if (currentQueryMetadata.getError() != null) {
// There is an error with one of the signals.
errorMessage = currentQueryMetadata.getError();
}
}
if (_placementSignalMap.size() > 0) {
JSONObject placementJSON = new JSONObject(_placementSignalMap);
_signalListener.onSignalsCollected(placementJSON.toString());
} else if (errorMessage == null){
_signalListener.onSignalsCollected("");
} else {
// If no signals could be generated, send SIGNALS_ERROR with the last error message
_signalListener.onSignalsCollectionFailed(errorMessage);
}
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
package com.unity3d.scar.adapter.v1920.signals;
import java.util.HashMap;
import java.util.Map;
public class SignalsStorage {
private Map<String, QueryInfoMetadata> _placementQueryInfoMap = new HashMap<>();
public Map<String, QueryInfoMetadata> getPlacementQueryInfoMap() {
return _placementQueryInfoMap;
}
public QueryInfoMetadata getQueryInfoMetadata(String placementId) {
return _placementQueryInfoMap.get(placementId);
}
public void put(String key, QueryInfoMetadata value) {
_placementQueryInfoMap.put(key, value);
}
}

Просмотреть файл

@ -0,0 +1,47 @@
package com.unity3d.scar.adapter.v1920.signals;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.HashMap;
import java.util.Map;
public class SignalsStorageTest {
private static final String TEST_PLACEMENTID = "placementId";
private static final String TEST_PLACEMENTID2 = "placementId2";
private QueryInfoMetadata queryInfoMetadataMock = Mockito.mock(QueryInfoMetadata.class);
@Test
public void testSignalStorage() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
QueryInfoMetadata queryInfoMetadata = signalsStorage.getQueryInfoMetadata(TEST_PLACEMENTID);
Assert.assertEquals(queryInfoMetadata, queryInfoMetadataMock);
}
@Test
public void testSignalStorageMultiplePlacements() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
signalsStorage.put(TEST_PLACEMENTID2, queryInfoMetadataMock);
Map<String, QueryInfoMetadata> expectedStorageContent = new HashMap<String, QueryInfoMetadata>() {
{
put(TEST_PLACEMENTID, queryInfoMetadataMock);
put(TEST_PLACEMENTID2, queryInfoMetadataMock);
}
};
Map<String, QueryInfoMetadata> queryInfoMetadataMap = signalsStorage.getPlacementQueryInfoMap();
Assert.assertEquals(expectedStorageContent, queryInfoMetadataMap);
}
@Test
public void testSignalStorageDuplicatePlacements() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
QueryInfoMetadata queryInfoMetadata = signalsStorage.getQueryInfoMetadata(TEST_PLACEMENTID);
Assert.assertEquals(queryInfoMetadata, queryInfoMetadataMock);
}
}

1
unity-scaradapter-1950/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/build

Просмотреть файл

@ -0,0 +1,62 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
testCoverageEnabled true
}
}
}
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation "org.mockito:mockito-android:2.25.0"
androidTestImplementation 'com.google.android.gms:play-services-ads:19.5.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation 'com.google.android.gms:play-services-ads:19.2.0'
api project(':unity-scaradapter-common')
compileOnly 'com.google.android.gms:play-services-ads:19.5.0'
}
task deleteOldJar(type: Delete) {
delete("../unity-ads/libs/${project.name}.jar")
}
task copyJars(type: Copy) {
from('build/intermediates/compile_library_classes_jar/release/')
into('../unity-ads/libs/')
include('classes.jar')
rename('classes.jar', "${project.name}.jar")
}
copyJars.dependsOn(deleteOldJar)
project.tasks.whenTaskAdded { Task theTask ->
if (theTask.name == 'bundleLibCompileToJarRelease') {
theTask.finalizedBy(copyJars)
}
}

Просмотреть файл

21
unity-scaradapter-1950/proguard-rules.pro поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Просмотреть файл

@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.scar.adapter.v1950">>
<application>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>
</application>
</manifest>

Просмотреть файл

@ -0,0 +1,13 @@
package com.unity3d.ads.test;
import com.unity3d.scar.adapter.v1950.signals.SignalsReaderTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
SignalsReaderTest.class
})
public class InstrumentationTestSuite {
}

Просмотреть файл

@ -0,0 +1,48 @@
package com.unity3d.scar.adapter.v1950.signals;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Matchers.any;
@RunWith(MockitoJUnitRunner.class)
public class SignalsReaderTest {
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
private ISignalCollectionListener _signalCollectionListener;
@Before
public void before() {
_signalCollectionListener = Mockito.mock(ISignalCollectionListener.class);
}
@Test
public void testGetScarSignals() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{"video"}, new String[]{"rewarded"}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
@Test
public void testGetScarSignalsNoRewarded() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{"video"}, new String[]{}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
@Test
public void testGetScarSignalsNoInterstitial() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{}, new String[]{"rewarded"}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.scar.adapter.v1950">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Просмотреть файл

@ -0,0 +1,59 @@
package com.unity3d.scar.adapter.v1950;
import android.content.Context;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.ScarAdapterBase;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1950.scarads.ScarInterstitialAd;
import com.unity3d.scar.adapter.v1950.scarads.ScarRewardedAd;
import com.unity3d.scar.adapter.v1950.signals.SignalsReader;
import com.unity3d.scar.adapter.v1950.signals.SignalsStorage;
import static com.unity3d.scar.adapter.common.Utils.runOnUiThread;
public class ScarAdapter extends ScarAdapterBase implements IScarAdapter {
private SignalsStorage _scarSignalStorage;
public ScarAdapter(IAdsErrorHandler adsErrorHandler) {
super(adsErrorHandler);
_scarSignalStorage = new SignalsStorage();
_scarSignalReader = new SignalsReader(_scarSignalStorage);
}
public void loadInterstitialAd(Context context, final ScarAdMetadata scarAd, final IScarInterstitialAdListenerWrapper adListenerWrapper) {
final ScarInterstitialAd interstitialAd = new ScarInterstitialAd(context, _scarSignalStorage.getQueryInfoMetadata(scarAd.getPlacementId()), scarAd, _adsErrorHandler, adListenerWrapper);
runOnUiThread(new Runnable() {
@Override
public void run() {
interstitialAd.loadAd(new IScarLoadListener() {
@Override
public void onAdLoaded() {
_loadedAds.put(scarAd.getPlacementId(), interstitialAd);
}
});
}
});
}
public void loadRewardedAd(Context context, final ScarAdMetadata scarAd, final IScarRewardedAdListenerWrapper adListenerWrapper) {
final ScarRewardedAd rewardedAd = new ScarRewardedAd(context, _scarSignalStorage.getQueryInfoMetadata(scarAd.getPlacementId()), scarAd, _adsErrorHandler, adListenerWrapper);
runOnUiThread(new Runnable() {
@Override
public void run() {
rewardedAd.loadAd(new IScarLoadListener() {
@Override
public void onAdLoaded() {
_loadedAds.put(scarAd.getPlacementId(), rewardedAd);
}
});
}
});
}
}

Просмотреть файл

@ -0,0 +1,41 @@
package com.unity3d.scar.adapter.v1950.scarads;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.query.AdInfo;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.scarads.IScarAd;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1950.signals.QueryInfoMetadata;
public abstract class ScarAdBase implements IScarAd {
protected Context _context;
protected ScarAdMetadata _scarAdMetadata;
protected QueryInfoMetadata _queryInfoMetadata;
protected IAdsErrorHandler _adsErrorHandler;
public ScarAdBase(Context context, ScarAdMetadata scarAdMetadata, QueryInfoMetadata queryInfoMetadata, IAdsErrorHandler adsErrorHandler) {
_context = context;
_scarAdMetadata = scarAdMetadata;
_queryInfoMetadata = queryInfoMetadata;
_adsErrorHandler = adsErrorHandler;
}
@Override
public void loadAd(IScarLoadListener loadListener) {
if (_queryInfoMetadata != null) {
AdInfo adInfo = new AdInfo(_queryInfoMetadata.getQueryInfo(), _scarAdMetadata.getAdString());
AdRequest adRequest = new AdRequest.Builder().setAdInfo(adInfo).build();
loadAdInternal(loadListener, adRequest);
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalLoadError(_scarAdMetadata));
}
}
protected abstract void loadAdInternal(IScarLoadListener loadListener, AdRequest adRequest);
}

Просмотреть файл

@ -0,0 +1,42 @@
package com.unity3d.scar.adapter.v1950.scarads;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1950.signals.QueryInfoMetadata;
public class ScarInterstitialAd extends ScarAdBase {
private InterstitialAd _interstitialAd;
private ScarInterstitialAdListener _interstitialAdDelegate;
public ScarInterstitialAd(Context context, QueryInfoMetadata queryInfoMetadata, ScarAdMetadata scarAdMetadata, IAdsErrorHandler adsErrorHandler, IScarInterstitialAdListenerWrapper adListener) {
super(context, scarAdMetadata, queryInfoMetadata, adsErrorHandler);
_interstitialAd = new InterstitialAd(_context);
_interstitialAd.setAdUnitId(_scarAdMetadata.getAdUnitId());
_interstitialAdDelegate = new ScarInterstitialAdListener(_interstitialAd, adListener);
}
@Override
public void loadAdInternal(IScarLoadListener loadListener, AdRequest adRequest) {
_interstitialAd.setAdListener(_interstitialAdDelegate.getAdListener());
_interstitialAdDelegate.setLoadListener(loadListener);
_interstitialAd.loadAd(adRequest);
}
@Override
public void show(Activity activity) {
if (_interstitialAd.isLoaded()) {
_interstitialAd.show();
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalShowError(_scarAdMetadata));
}
}
}

Просмотреть файл

@ -0,0 +1,63 @@
package com.unity3d.scar.adapter.v1950.scarads;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.ads.LoadAdError;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
public class ScarInterstitialAdListener {
private InterstitialAd _interstitialAd;
private IScarInterstitialAdListenerWrapper _adListenerWrapper;
private IScarLoadListener _loadListener;
public ScarInterstitialAdListener(InterstitialAd interstitialAd, IScarInterstitialAdListenerWrapper adListenerWrapper) {
_interstitialAd = interstitialAd;
_adListenerWrapper = adListenerWrapper;
}
private AdListener _adListener = new AdListener() {
@Override
public void onAdLoaded() {
_adListenerWrapper.onAdLoaded();
if (_loadListener != null) {
_loadListener.onAdLoaded();
}
}
@Override
public void onAdFailedToLoad(LoadAdError adErrorCode) {
_adListenerWrapper.onAdFailedToLoad(adErrorCode.getCode(), adErrorCode.toString());
}
@Override
public void onAdOpened() {
_adListenerWrapper.onAdOpened();
}
@Override
public void onAdClicked() {
_adListenerWrapper.onAdClicked();
}
@Override
public void onAdLeftApplication() {
_adListenerWrapper.onAdLeftApplication();
}
@Override
public void onAdClosed() {
_adListenerWrapper.onAdClosed();
}
};
public AdListener getAdListener() {
return _adListener;
}
public void setLoadListener(IScarLoadListener loadListener) {
_loadListener = loadListener;
}
}

Просмотреть файл

@ -0,0 +1,41 @@
package com.unity3d.scar.adapter.v1950.scarads;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v1950.signals.QueryInfoMetadata;
public class ScarRewardedAd extends ScarAdBase {
private RewardedAd _rewardedAd;
private ScarRewardedAdListener _rewardedAdDelegate;
public ScarRewardedAd(Context context, QueryInfoMetadata queryInfoMetadata, ScarAdMetadata scarAdMetadata, IAdsErrorHandler adsErrorHandler, IScarRewardedAdListenerWrapper adListener) {
super(context, scarAdMetadata, queryInfoMetadata, adsErrorHandler);
_rewardedAd = new RewardedAd(_context, _scarAdMetadata.getAdUnitId());
_rewardedAdDelegate = new ScarRewardedAdListener(_rewardedAd, adListener);
}
@Override
public void loadAdInternal(IScarLoadListener loadListener, AdRequest adRequest) {
_rewardedAdDelegate.setLoadListener(loadListener);
_rewardedAd.loadAd(adRequest, _rewardedAdDelegate.getRewardedAdLoadCallback());
}
@Override
public void show(Activity activity) {
if (_rewardedAd.isLoaded()) {
_rewardedAd.show(activity, _rewardedAdDelegate.getRewardedAdCallback());
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalShowError(_scarAdMetadata));
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
package com.unity3d.scar.adapter.v1950.scarads;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.rewarded.RewardItem;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.google.android.gms.ads.rewarded.RewardedAdCallback;
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
public class ScarRewardedAdListener {
private RewardedAd _rewardedAd;
private IScarRewardedAdListenerWrapper _adListenerWrapper;
private IScarLoadListener _loadListener;
public ScarRewardedAdListener(RewardedAd rewardedAd, IScarRewardedAdListenerWrapper adListenerWrapper) {
_rewardedAd = rewardedAd;
_adListenerWrapper = adListenerWrapper;
}
private RewardedAdLoadCallback _rewardedAdLoadCallback = new RewardedAdLoadCallback() {
@Override
public void onRewardedAdLoaded() {
_adListenerWrapper.onRewardedAdLoaded();
if (_loadListener != null) {
_loadListener.onAdLoaded();
}
}
@Override
public void onRewardedAdFailedToLoad(LoadAdError adError) {
_adListenerWrapper.onRewardedAdFailedToLoad(adError.getCode(), adError.toString());
}
};
private RewardedAdCallback rewardedAdCallback = new RewardedAdCallback () {
@Override
public void onRewardedAdOpened() {
_adListenerWrapper.onRewardedAdOpened();
}
@Override
public void onRewardedAdFailedToShow(AdError adError) {
_adListenerWrapper.onRewardedAdFailedToShow(adError.getCode(), adError.toString());
}
@Override
public void onUserEarnedReward(RewardItem rewardItem) {
_adListenerWrapper.onUserEarnedReward();
}
@Override
public void onRewardedAdClosed() {
_adListenerWrapper.onRewardedAdClosed();
}
};
public RewardedAdCallback getRewardedAdCallback() {
return rewardedAdCallback;
}
public RewardedAdLoadCallback getRewardedAdLoadCallback() { return _rewardedAdLoadCallback; }
public void setLoadListener(IScarLoadListener loadListener) {
_loadListener = loadListener;
}
}

Просмотреть файл

@ -0,0 +1,33 @@
package com.unity3d.scar.adapter.v1950.signals;
import android.util.Log;
import com.google.android.gms.ads.query.QueryInfo;
import com.google.android.gms.ads.query.QueryInfoGenerationCallback;
import com.unity3d.scar.adapter.common.DispatchGroup;
import com.unity3d.scar.adapter.v1950.ScarAdapter;
public class QueryInfoCallback extends QueryInfoGenerationCallback {
private DispatchGroup _dispatchGroup;
private QueryInfoMetadata _gmaQueryInfoMetadata;
public QueryInfoCallback(final QueryInfoMetadata gmaQueryInfoMetadata, final DispatchGroup dispatchGroup) {
_dispatchGroup = dispatchGroup;
_gmaQueryInfoMetadata = gmaQueryInfoMetadata;
}
// Called when QueryInfo generation succeeds
@Override
public void onSuccess(final QueryInfo queryInfo) {
_gmaQueryInfoMetadata.setQueryInfo(queryInfo);
_dispatchGroup.leave();
}
// Called when QueryInfo generation fails
@Override
public void onFailure(String failureMsg) {
_gmaQueryInfoMetadata.setError(failureMsg);
_dispatchGroup.leave();
}
}

Просмотреть файл

@ -0,0 +1,41 @@
package com.unity3d.scar.adapter.v1950.signals;
import com.google.android.gms.ads.query.QueryInfo;
public class QueryInfoMetadata {
private String _placementId;
private QueryInfo _queryInfo;
private String _error;
public QueryInfoMetadata(String placementId) {
_placementId = placementId;
}
public String getPlacementId() {
return _placementId;
}
public QueryInfo getQueryInfo() {
return _queryInfo;
}
public String getQueryStr() {
String query = null;
if (_queryInfo != null) {
query = _queryInfo.getQuery();
}
return query;
}
public String getError() {
return _error;
}
public void setQueryInfo(QueryInfo queryInfo) {
_queryInfo = queryInfo;
}
public void setError(String error) {
_error = error;
}
}

Просмотреть файл

@ -0,0 +1,84 @@
package com.unity3d.scar.adapter.v1950.signals;
import android.content.Context;
import com.google.android.gms.ads.AdFormat;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.query.QueryInfo;
import com.unity3d.scar.adapter.common.DispatchGroup;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import com.unity3d.scar.adapter.common.signals.ISignalsReader;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class SignalsReader implements ISignalsReader {
private static SignalsStorage _signalsStorage;
public SignalsReader(SignalsStorage signalsStorage) {
_signalsStorage = signalsStorage;
}
public void getSCARSignals(Context context, String[] interstitialList, String[] rewardedList,
ISignalCollectionListener signalCompletionListener) {
DispatchGroup dispatchGroup = new DispatchGroup();
for (String interstitialId : interstitialList) {
dispatchGroup.enter();
getSCARSignal(context, interstitialId, AdFormat.INTERSTITIAL, dispatchGroup);
}
for (String rewardedId : rewardedList) {
dispatchGroup.enter();
getSCARSignal(context, rewardedId, AdFormat.REWARDED, dispatchGroup);
}
dispatchGroup.notify(new GMAScarDispatchCompleted(signalCompletionListener));
}
private void getSCARSignal(Context context, String placementId, AdFormat adType, DispatchGroup dispatchGroup) {
AdRequest request = new AdRequest.Builder().build();
QueryInfoMetadata gmaQueryInfoMetadata = new QueryInfoMetadata(placementId);
QueryInfoCallback gmaQueryInfoCallback = new QueryInfoCallback(gmaQueryInfoMetadata, dispatchGroup);
// Callback on the QueryInfoCallback is async here.
_signalsStorage.put(placementId, gmaQueryInfoMetadata);
QueryInfo.generate(context, adType, request, gmaQueryInfoCallback);
}
private class GMAScarDispatchCompleted implements Runnable {
private ISignalCollectionListener _signalListener;
public GMAScarDispatchCompleted(ISignalCollectionListener signalListener) {
_signalListener = signalListener;
}
@Override
public void run() {
// Called once every dispatched thread has returned.
// Build up the JSON response since we received all the signals.
Map<String, String> _placementSignalMap = new HashMap<>();
String errorMessage = null;
for(Map.Entry<String, QueryInfoMetadata> queryInfoMetadata : _signalsStorage.getPlacementQueryInfoMap().entrySet()) {
QueryInfoMetadata currentQueryMetadata = queryInfoMetadata.getValue();
_placementSignalMap.put(currentQueryMetadata.getPlacementId(), currentQueryMetadata.getQueryStr());
if (currentQueryMetadata.getError() != null) {
// There is an error with one of the signals.
errorMessage = currentQueryMetadata.getError();
}
}
if (_placementSignalMap.size() > 0) {
JSONObject placementJSON = new JSONObject(_placementSignalMap);
_signalListener.onSignalsCollected(placementJSON.toString());
} else if (errorMessage == null){
_signalListener.onSignalsCollected("");
} else {
// If no signals could be generated, send SIGNALS_ERROR with the last error message
_signalListener.onSignalsCollectionFailed(errorMessage);
}
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
package com.unity3d.scar.adapter.v1950.signals;
import java.util.HashMap;
import java.util.Map;
public class SignalsStorage {
private Map<String, QueryInfoMetadata> _placementQueryInfoMap = new HashMap<>();
public Map<String, QueryInfoMetadata> getPlacementQueryInfoMap() {
return _placementQueryInfoMap;
}
public QueryInfoMetadata getQueryInfoMetadata(String placementId) {
return _placementQueryInfoMap.get(placementId);
}
public void put(String key, QueryInfoMetadata value) {
_placementQueryInfoMap.put(key, value);
}
}

Просмотреть файл

@ -0,0 +1,49 @@
package com.unity3d.scar.adapter.v1950.signals;
import android.app.DownloadManager;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.HashMap;
import java.util.Map;
public class SignalsStorageTest {
private static final String TEST_PLACEMENTID = "placementId";
private static final String TEST_PLACEMENTID2 = "placementId2";
private QueryInfoMetadata queryInfoMetadataMock = Mockito.mock(QueryInfoMetadata.class);
@Test
public void testSignalStorage() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
QueryInfoMetadata queryInfoMetadata = signalsStorage.getQueryInfoMetadata(TEST_PLACEMENTID);
Assert.assertEquals(queryInfoMetadata, queryInfoMetadataMock);
}
@Test
public void testSignalStorageMultiplePlacements() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
signalsStorage.put(TEST_PLACEMENTID2, queryInfoMetadataMock);
Map<String, QueryInfoMetadata> expectedStorageContent = new HashMap<String, QueryInfoMetadata>() {
{
put(TEST_PLACEMENTID, queryInfoMetadataMock);
put(TEST_PLACEMENTID2, queryInfoMetadataMock);
}
};
Map<String, QueryInfoMetadata> queryInfoMetadataMap = signalsStorage.getPlacementQueryInfoMap();
Assert.assertEquals(expectedStorageContent, queryInfoMetadataMap);
}
@Test
public void testSignalStorageDuplicatePlacements() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
signalsStorage.put(TEST_PLACEMENTID, queryInfoMetadataMock);
QueryInfoMetadata queryInfoMetadata = signalsStorage.getQueryInfoMetadata(TEST_PLACEMENTID);
Assert.assertEquals(queryInfoMetadata, queryInfoMetadataMock);
}
}

1
unity-scaradapter-2000/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/build

Просмотреть файл

@ -0,0 +1,62 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
testCoverageEnabled true
}
}
}
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation "org.mockito:mockito-android:2.25.0"
androidTestImplementation 'com.google.android.gms:play-services-ads:20.1.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:2.28.2'
testImplementation 'com.google.android.gms:play-services-ads:20.1.0'
api project(':unity-scaradapter-common')
compileOnly 'com.google.android.gms:play-services-ads:20.1.0'
}
task deleteOldJar(type: Delete) {
delete("../unity-ads/libs/${project.name}.jar")
}
task copyJars(type: Copy) {
from('build/intermediates/compile_library_classes_jar/release/')
into('../unity-ads/libs/')
include('classes.jar')
rename('classes.jar', "${project.name}.jar")
}
copyJars.dependsOn(deleteOldJar)
project.tasks.whenTaskAdded { Task theTask ->
if (theTask.name == 'bundleLibCompileToJarRelease') {
theTask.finalizedBy(copyJars)
}
}

21
unity-scaradapter-2000/proguard-rules.pro поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Просмотреть файл

@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.scar.adapter.v2000">>
<application>
<activity android:name="android.app.Activity" />
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>
</application>
</manifest>

Просмотреть файл

@ -0,0 +1,13 @@
package com.unity3d.ads.test;
import com.unity3d.scar.adapter.v2000.signals.SignalsReaderTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
SignalsReaderTest.class
})
public class InstrumentationTestSuite {
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,101 @@
package com.unity3d.scar.adapter.v2000;
import android.app.Activity;
import android.content.Context;
import androidx.test.core.app.ActivityScenario;
import androidx.test.platform.app.InstrumentationRegistry;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.GMAEvent;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static com.unity3d.scar.adapter.v2000.Constants.SCARExampleAdUnitId;
import static com.unity3d.scar.adapter.v2000.Constants.SCARExampleInterstitialAdString;
@RunWith(MockitoJUnitRunner.class)
public class ScarAdapterTest {
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
@Mock
IAdsErrorHandler _adsErrorHandlerMock;
private ScarAdapter _scarAdapter = new ScarAdapter(_adsErrorHandlerMock);
@Mock
ISignalCollectionListener _signalCollectionListenerMock;
@Mock
IScarInterstitialAdListenerWrapper _scarInterstitialAdListenerWrapperMock;
@Test
public void testScarAdapterGetSignals() {
_scarAdapter.getSCARSignals(context, new String[]{"video"}, new String[]{"rewarded"}, _signalCollectionListenerMock);
Mockito.verify(_signalCollectionListenerMock, Mockito.timeout(1000).times(1)).onSignalsCollected(Mockito.anyString());
}
@Test
public void testScarAdapterGetSignalsEmptyInterstitialPlacement() {
_scarAdapter.getSCARSignals(context, new String[0], new String[]{"rewarded"}, _signalCollectionListenerMock);
Mockito.verify(_signalCollectionListenerMock, Mockito.timeout(1000).times(1)).onSignalsCollected(Mockito.anyString());
}
@Test
public void testScarAdapterGetSignalsEmptyRewardedPlacement() {
_scarAdapter.getSCARSignals(context, new String[]{"video"}, new String[0], _signalCollectionListenerMock);
Mockito.verify(_signalCollectionListenerMock, Mockito.timeout(1000).times(1)).onSignalsCollected(Mockito.anyString());
}
@Test
public void testScarAdapterLoad() {
_scarAdapter.getSCARSignals(context, new String[]{"video"}, new String[0], _signalCollectionListenerMock);
Mockito.verify(_signalCollectionListenerMock, Mockito.timeout(1000).times(1)).onSignalsCollected(Mockito.anyString());
_scarAdapter.loadInterstitialAd(context, getDefaultScarMeta(), _scarInterstitialAdListenerWrapperMock);
Mockito.verify(_scarInterstitialAdListenerWrapperMock, Mockito.timeout(5000).times(1)).onAdLoaded();
}
@Test
public void testScarAdapterLoadAndShow() {
_scarAdapter.getSCARSignals(context, new String[]{"video"}, new String[0], _signalCollectionListenerMock);
Mockito.verify(_signalCollectionListenerMock, Mockito.timeout(1000).times(1)).onSignalsCollected(Mockito.anyString());
_scarAdapter.loadInterstitialAd(context, getDefaultScarMeta(), _scarInterstitialAdListenerWrapperMock);
Mockito.verify(_scarInterstitialAdListenerWrapperMock, Mockito.timeout(5000).times(1)).onAdLoaded();
try(ActivityScenario<Activity> scenario = ActivityScenario.launch(Activity.class)) {
scenario.onActivity(new ActivityScenario.ActivityAction<Activity>() {
@Override
public void perform(Activity activity) {
_scarAdapter.show(activity, "", "video");
}
});
}
Mockito.verify(_scarInterstitialAdListenerWrapperMock, Mockito.timeout(20000).times(1)).onAdOpened();
Mockito.verify(_scarInterstitialAdListenerWrapperMock, Mockito.timeout(20000).times(1)).onAdImpression();
}
@Test
public void testScarAdapterShowWithoutLoad() {
_scarAdapter = new ScarAdapter(_adsErrorHandlerMock);
try(ActivityScenario<Activity> scenario = ActivityScenario.launch(Activity.class)) {
scenario.onActivity(new ActivityScenario.ActivityAction<Activity>() {
@Override
public void perform(Activity activity) {
_scarAdapter.show(activity, "", "video");
}
});
}
Mockito.verify(_adsErrorHandlerMock, Mockito.timeout(20000).times(1)).handleError(Mockito.any(GMAAdsError.class));
}
private ScarAdMetadata getDefaultScarMeta() {
return new ScarAdMetadata("video", "", SCARExampleAdUnitId, SCARExampleInterstitialAdString, 30);
}
}

Просмотреть файл

@ -0,0 +1,39 @@
package com.unity3d.scar.adapter.v2000.signals;
import com.google.android.gms.ads.query.QueryInfo;
import com.unity3d.scar.adapter.common.DispatchGroup;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class QueryInfoCallbackTest {
@Mock
QueryInfoMetadata _queryInfoMetadataMock;
@Mock
DispatchGroup _dispatchGroupMock;
@Mock
QueryInfo _queryInfoMock;
@Test
public void testQueryInfoCallbackSuccess() {
QueryInfoCallback queryInfoCallback = new QueryInfoCallback(_queryInfoMetadataMock, _dispatchGroupMock);
queryInfoCallback.onSuccess(_queryInfoMock);
Mockito.verify(_dispatchGroupMock, Mockito.times(1)).leave();
Mockito.verify(_queryInfoMetadataMock, Mockito.times(1)).setQueryInfo(_queryInfoMock);
}
@Test
public void testQueryInfoCallbackError() {
QueryInfoCallback queryInfoCallback = new QueryInfoCallback(_queryInfoMetadataMock, _dispatchGroupMock);
queryInfoCallback.onFailure("");
Mockito.verify(_dispatchGroupMock, Mockito.times(1)).leave();
Mockito.verify(_queryInfoMetadataMock, Mockito.times(1)).setError(Mockito.anyString());
}
}

Просмотреть файл

@ -0,0 +1,48 @@
package com.unity3d.scar.adapter.v2000.signals;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Matchers.any;
@RunWith(MockitoJUnitRunner.class)
public class SignalsReaderTest {
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
private ISignalCollectionListener _signalCollectionListener;
@Before
public void before() {
_signalCollectionListener = Mockito.mock(ISignalCollectionListener.class);
}
@Test
public void testGetScarSignals() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{"video"}, new String[]{"rewarded"}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
@Test
public void testGetScarSignalsNoRewarded() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{"video"}, new String[]{}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
@Test
public void testGetScarSignalsNoInterstitial() {
SignalsReader signalsReader = new SignalsReader(new SignalsStorage());
signalsReader.getSCARSignals(context, new String[]{}, new String[]{"rewarded"}, _signalCollectionListener);
Mockito.verify(_signalCollectionListener, Mockito.timeout(5000).times(1)).onSignalsCollected(any(String.class));
}
}

Просмотреть файл

@ -0,0 +1,21 @@
package com.unity3d.scar.adapter.v2000.signals;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class SignalsStorageTest {
@Mock
QueryInfoMetadata _queryInfoMetadaMock;
@Test
public void testSignalStorage() {
SignalsStorage signalsStorage = new SignalsStorage();
signalsStorage.put("video", _queryInfoMetadaMock);
QueryInfoMetadata storedMetadata = signalsStorage.getQueryInfoMetadata("video");
Assert.assertEquals(_queryInfoMetadaMock, storedMetadata);
}
}

Просмотреть файл

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.scar.adapter.v2000">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Просмотреть файл

@ -0,0 +1,59 @@
package com.unity3d.scar.adapter.v2000;
import android.content.Context;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.ScarAdapterBase;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v2000.scarads.ScarInterstitialAd;
import com.unity3d.scar.adapter.v2000.scarads.ScarRewardedAd;
import com.unity3d.scar.adapter.v2000.signals.SignalsReader;
import com.unity3d.scar.adapter.v2000.signals.SignalsStorage;
import static com.unity3d.scar.adapter.common.Utils.runOnUiThread;
public class ScarAdapter extends ScarAdapterBase implements IScarAdapter {
private SignalsStorage _scarSignalStorage;
public ScarAdapter(IAdsErrorHandler adsErrorHandler) {
super(adsErrorHandler);
_scarSignalStorage = new SignalsStorage();
_scarSignalReader = new SignalsReader(_scarSignalStorage);
}
public void loadInterstitialAd(Context context, final ScarAdMetadata scarAd, final IScarInterstitialAdListenerWrapper adListenerWrapper) {
final ScarInterstitialAd interstitialAd = new ScarInterstitialAd(context, _scarSignalStorage.getQueryInfoMetadata(scarAd.getPlacementId()), scarAd, _adsErrorHandler, adListenerWrapper);
runOnUiThread(new Runnable() {
@Override
public void run() {
interstitialAd.loadAd(new IScarLoadListener() {
@Override
public void onAdLoaded() {
_loadedAds.put(scarAd.getPlacementId(), interstitialAd);
}
});
}
});
}
public void loadRewardedAd(Context context, final ScarAdMetadata scarAd, final IScarRewardedAdListenerWrapper adListenerWrapper) {
final ScarRewardedAd rewardedAd = new ScarRewardedAd(context, _scarSignalStorage.getQueryInfoMetadata(scarAd.getPlacementId()), scarAd, _adsErrorHandler, adListenerWrapper);
runOnUiThread(new Runnable() {
@Override
public void run() {
rewardedAd.loadAd(new IScarLoadListener() {
@Override
public void onAdLoaded() {
_loadedAds.put(scarAd.getPlacementId(), rewardedAd);
}
});
}
});
}
}

Просмотреть файл

@ -0,0 +1,48 @@
package com.unity3d.scar.adapter.v2000.scarads;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.query.AdInfo;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.scarads.IScarAd;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v2000.signals.QueryInfoMetadata;
public abstract class ScarAdBase<T> implements IScarAd {
protected T _adObj;
protected Context _context;
protected ScarAdMetadata _scarAdMetadata;
protected QueryInfoMetadata _queryInfoMetadata;
protected ScarAdListener _scarAdListener;
protected IAdsErrorHandler _adsErrorHandler;
public ScarAdBase(Context context, ScarAdMetadata scarAdMetadata, QueryInfoMetadata queryInfoMetadata, IAdsErrorHandler adsErrorHandler) {
_context = context;
_scarAdMetadata = scarAdMetadata;
_queryInfoMetadata = queryInfoMetadata;
_adsErrorHandler = adsErrorHandler;
}
public void setGmaAd(T rewardedAd) {
_adObj = rewardedAd;
}
@Override
public void loadAd(IScarLoadListener loadListener) {
if (_queryInfoMetadata != null) {
AdInfo adInfo = new AdInfo(_queryInfoMetadata.getQueryInfo(), _scarAdMetadata.getAdString());
AdRequest adRequest = new AdRequest.Builder().setAdInfo(adInfo).build();
_scarAdListener.setLoadListener(loadListener);
loadAdInternal(adRequest, loadListener);
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalLoadError(_scarAdMetadata));
}
}
protected abstract void loadAdInternal(AdRequest adRequest, IScarLoadListener loadListener);
}

Просмотреть файл

@ -0,0 +1,12 @@
package com.unity3d.scar.adapter.v2000.scarads;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
public class ScarAdListener {
protected IScarLoadListener _loadListener;
public void setLoadListener(IScarLoadListener loadListener) {
_loadListener = loadListener;
}
}

Просмотреть файл

@ -0,0 +1,35 @@
package com.unity3d.scar.adapter.v2000.scarads;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.interstitial.InterstitialAd;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v2000.signals.QueryInfoMetadata;
public class ScarInterstitialAd extends ScarAdBase<InterstitialAd> {
public ScarInterstitialAd(Context context, QueryInfoMetadata queryInfoMetadata, ScarAdMetadata scarAdMetadata, IAdsErrorHandler adsErrorHandler, IScarInterstitialAdListenerWrapper adListener) {
super(context, scarAdMetadata, queryInfoMetadata, adsErrorHandler);
_scarAdListener = new ScarInterstitialAdListener(adListener, this);
}
@Override
protected void loadAdInternal(AdRequest adRequest, IScarLoadListener loadListener) {
InterstitialAd.load(_context, _scarAdMetadata.getAdUnitId(), adRequest, ((ScarInterstitialAdListener)_scarAdListener).getAdLoadListener());
}
@Override
public void show(Activity activity) {
if (_adObj != null) {
_adObj.show(activity);
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalShowError(_scarAdMetadata));
}
}
}

Просмотреть файл

@ -0,0 +1,70 @@
package com.unity3d.scar.adapter.v2000.scarads;
import androidx.annotation.NonNull;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.interstitial.InterstitialAd;
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
public class ScarInterstitialAdListener extends ScarAdListener {
private final ScarInterstitialAd _scarInterstitialAd;
private final IScarInterstitialAdListenerWrapper _adListenerWrapper;
public ScarInterstitialAdListener(IScarInterstitialAdListenerWrapper adListenerWrapper, ScarInterstitialAd scarInterstitialAd) {
_adListenerWrapper = adListenerWrapper;
_scarInterstitialAd = scarInterstitialAd;
}
private final InterstitialAdLoadCallback _adLoadCallback = new InterstitialAdLoadCallback() {
@Override
public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {
super.onAdLoaded(interstitialAd);
_adListenerWrapper.onAdLoaded();
interstitialAd.setFullScreenContentCallback(_fullScreenContentCallback);
_scarInterstitialAd.setGmaAd(interstitialAd);
if (_loadListener != null) {
_loadListener.onAdLoaded();
}
}
@Override
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
super.onAdFailedToLoad(loadAdError);
_adListenerWrapper.onAdFailedToLoad(loadAdError.getCode(), loadAdError.toString());
}
};
private final FullScreenContentCallback _fullScreenContentCallback = new FullScreenContentCallback() {
@Override
public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) {
super.onAdFailedToShowFullScreenContent(adError);
_adListenerWrapper.onAdFailedToShow(adError.getCode(), adError.toString());
}
@Override
public void onAdShowedFullScreenContent() {
super.onAdShowedFullScreenContent();
_adListenerWrapper.onAdOpened();
}
@Override
public void onAdDismissedFullScreenContent() {
super.onAdDismissedFullScreenContent();
_adListenerWrapper.onAdClosed();
}
@Override
public void onAdImpression() {
super.onAdImpression();
_adListenerWrapper.onAdImpression();
}
};
public InterstitialAdLoadCallback getAdLoadListener() {
return _adLoadCallback;
}
}

Просмотреть файл

@ -0,0 +1,36 @@
package com.unity3d.scar.adapter.v2000.scarads;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.unity3d.scar.adapter.common.GMAAdsError;
import com.unity3d.scar.adapter.common.IAdsErrorHandler;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.IScarLoadListener;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.v2000.signals.QueryInfoMetadata;
public class ScarRewardedAd extends ScarAdBase<RewardedAd> {
public ScarRewardedAd(Context context, QueryInfoMetadata queryInfoMetadata, ScarAdMetadata scarAdMetadata, IAdsErrorHandler adsErrorHandler, IScarRewardedAdListenerWrapper adListener) {
super(context, scarAdMetadata, queryInfoMetadata, adsErrorHandler);
_scarAdListener = new ScarRewardedAdListener(adListener, this);
}
@Override
protected void loadAdInternal(AdRequest adRequest, IScarLoadListener loadListener) {
RewardedAd.load(_context, _scarAdMetadata.getAdUnitId(), adRequest, ((ScarRewardedAdListener)_scarAdListener).getAdLoadListener());
}
@Override
public void show(Activity activity) {
if (_adObj != null) {
_adObj.show(activity, ((ScarRewardedAdListener)_scarAdListener).getOnUserEarnedRewardListener());
} else {
_adsErrorHandler.handleError(GMAAdsError.InternalShowError(_scarAdMetadata));
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
package com.unity3d.scar.adapter.v2000.scarads;
import androidx.annotation.NonNull;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.OnUserEarnedRewardListener;
import com.google.android.gms.ads.rewarded.RewardItem;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
public class ScarRewardedAdListener extends ScarAdListener {
private final ScarRewardedAd _scarRewardedAd;
private final IScarRewardedAdListenerWrapper _adListenerWrapper;
public ScarRewardedAdListener(IScarRewardedAdListenerWrapper adListenerWrapper, ScarRewardedAd scarRewardedAd) {
_adListenerWrapper = adListenerWrapper;
_scarRewardedAd = scarRewardedAd;
}
private final RewardedAdLoadCallback _adLoadCallback = new RewardedAdLoadCallback() {
@Override
public void onAdLoaded(@NonNull RewardedAd rewardedAd) {
super.onAdLoaded(rewardedAd);
_adListenerWrapper.onRewardedAdLoaded();
rewardedAd.setFullScreenContentCallback(_fullScreenContentCallback);
_scarRewardedAd.setGmaAd(rewardedAd);
if (_loadListener != null) {
_loadListener.onAdLoaded();
}
}
@Override
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
super.onAdFailedToLoad(loadAdError);
_adListenerWrapper.onRewardedAdFailedToLoad(loadAdError.getCode(), loadAdError.toString());
}
};
private final OnUserEarnedRewardListener _onUserEarnedRewardListener = new OnUserEarnedRewardListener() {
@Override
public void onUserEarnedReward(@NonNull RewardItem rewardItem) {
_adListenerWrapper.onUserEarnedReward();
}
};
private final FullScreenContentCallback _fullScreenContentCallback = new FullScreenContentCallback() {
@Override
public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) {
super.onAdFailedToShowFullScreenContent(adError);
_adListenerWrapper.onRewardedAdFailedToShow(adError.getCode(), adError.toString());
}
@Override
public void onAdShowedFullScreenContent() {
super.onAdShowedFullScreenContent();
_adListenerWrapper.onRewardedAdOpened();
}
@Override
public void onAdDismissedFullScreenContent() {
super.onAdDismissedFullScreenContent();
_adListenerWrapper.onRewardedAdClosed();
}
@Override
public void onAdImpression() {
super.onAdImpression();
_adListenerWrapper.onAdImpression();
}
};
public OnUserEarnedRewardListener getOnUserEarnedRewardListener() {
return _onUserEarnedRewardListener;
}
public RewardedAdLoadCallback getAdLoadListener() {
return _adLoadCallback;
}
}

Просмотреть файл

@ -0,0 +1,33 @@
package com.unity3d.scar.adapter.v2000.signals;
import android.util.Log;
import com.google.android.gms.ads.query.QueryInfo;
import com.google.android.gms.ads.query.QueryInfoGenerationCallback;
import com.unity3d.scar.adapter.common.DispatchGroup;
import com.unity3d.scar.adapter.v2000.ScarAdapter;
public class QueryInfoCallback extends QueryInfoGenerationCallback {
private DispatchGroup _dispatchGroup;
private QueryInfoMetadata _gmaQueryInfoMetadata;
public QueryInfoCallback(final QueryInfoMetadata gmaQueryInfoMetadata, final DispatchGroup dispatchGroup) {
_dispatchGroup = dispatchGroup;
_gmaQueryInfoMetadata = gmaQueryInfoMetadata;
}
// Called when QueryInfo generation succeeds
@Override
public void onSuccess(final QueryInfo queryInfo) {
_gmaQueryInfoMetadata.setQueryInfo(queryInfo);
_dispatchGroup.leave();
}
// Called when QueryInfo generation fails
@Override
public void onFailure(String failureMsg) {
_gmaQueryInfoMetadata.setError(failureMsg);
_dispatchGroup.leave();
}
}

Просмотреть файл

@ -0,0 +1,41 @@
package com.unity3d.scar.adapter.v2000.signals;
import com.google.android.gms.ads.query.QueryInfo;
public class QueryInfoMetadata {
private String _placementId;
private QueryInfo _queryInfo;
private String _error;
public QueryInfoMetadata(String placementId) {
_placementId = placementId;
}
public String getPlacementId() {
return _placementId;
}
public QueryInfo getQueryInfo() {
return _queryInfo;
}
public String getQueryStr() {
String query = null;
if (_queryInfo != null) {
query = _queryInfo.getQuery();
}
return query;
}
public String getError() {
return _error;
}
public void setQueryInfo(QueryInfo queryInfo) {
_queryInfo = queryInfo;
}
public void setError(String error) {
_error = error;
}
}

Просмотреть файл

@ -0,0 +1,86 @@
package com.unity3d.scar.adapter.v2000.signals;
import android.content.Context;
import com.google.android.gms.ads.AdFormat;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.query.QueryInfo;
import com.unity3d.scar.adapter.common.DispatchGroup;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import com.unity3d.scar.adapter.common.signals.ISignalsReader;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class SignalsReader implements ISignalsReader {
private static Map<String, String> _placementSignalMap;
private static SignalsStorage _signalsStorage;
public SignalsReader(SignalsStorage signalsStorage) {
_signalsStorage = signalsStorage;
}
@Override
public void getSCARSignals(Context context, String[] interstitialList, String[] rewardedList,
ISignalCollectionListener signalCompletionListener) {
DispatchGroup dispatchGroup = new DispatchGroup();
for (String interstitialId : interstitialList) {
dispatchGroup.enter();
getSCARSignal(context, interstitialId, AdFormat.INTERSTITIAL, dispatchGroup);
}
for (String rewardedId : rewardedList) {
dispatchGroup.enter();
getSCARSignal(context, rewardedId, AdFormat.REWARDED, dispatchGroup);
}
dispatchGroup.notify(new GMAScarDispatchCompleted(signalCompletionListener));
}
private void getSCARSignal(Context context, String placementId, AdFormat adType, DispatchGroup dispatchGroup) {
AdRequest request = new AdRequest.Builder().build();
QueryInfoMetadata gmaQueryInfoMetadata = new QueryInfoMetadata(placementId);
QueryInfoCallback gmaQueryInfoCallback = new QueryInfoCallback(gmaQueryInfoMetadata, dispatchGroup);
// Callback on the QueryInfoCallback is async here.
_signalsStorage.put(placementId, gmaQueryInfoMetadata);
QueryInfo.generate(context, adType, request, gmaQueryInfoCallback);
}
private class GMAScarDispatchCompleted implements Runnable {
private ISignalCollectionListener _signalListener;
public GMAScarDispatchCompleted(ISignalCollectionListener signalListener) {
_signalListener = signalListener;
}
@Override
public void run() {
// Called once every dispatched thread has returned.
// Build up the JSON response since we received all the signals.
_placementSignalMap = new HashMap<>();
String errorMessage = null;
for(Map.Entry<String, QueryInfoMetadata> queryInfoMetadata : _signalsStorage.getPlacementQueryInfoMap().entrySet()) {
QueryInfoMetadata currentQueryMetadata = queryInfoMetadata.getValue();
_placementSignalMap.put(currentQueryMetadata.getPlacementId(), currentQueryMetadata.getQueryStr());
if (currentQueryMetadata.getError() != null) {
// There is an error with one of the signals.
errorMessage = currentQueryMetadata.getError();
}
}
if (_placementSignalMap.size() > 0) {
JSONObject placementJSON = new JSONObject(_placementSignalMap);
_signalListener.onSignalsCollected(placementJSON.toString());
} else if (errorMessage == null){
_signalListener.onSignalsCollected("");
} else {
// If no signals could be generated, send SIGNALS_ERROR with the last error message
_signalListener.onSignalsCollectionFailed(errorMessage);
}
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
package com.unity3d.scar.adapter.v2000.signals;
import java.util.HashMap;
import java.util.Map;
public class SignalsStorage {
private Map<String, QueryInfoMetadata> _placementQueryInfoMap = new HashMap<>();
public Map<String, QueryInfoMetadata> getPlacementQueryInfoMap() {
return _placementQueryInfoMap;
}
public QueryInfoMetadata getQueryInfoMetadata(String placementId) {
return _placementQueryInfoMap.get(placementId);
}
public void put(String key, QueryInfoMetadata value) {
_placementQueryInfoMap.put(key, value);
}
}

1
unity-scaradapter-common/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
/build

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше