This commit is contained in:
runner 2023-09-28 00:55:34 +00:00
Родитель 4f6c0f610f
Коммит 9030d0ca27
183 изменённых файлов: 2800 добавлений и 2337 удалений

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

@ -7,8 +7,8 @@ android {
applicationId "com.unity3d.ads.example"
minSdkVersion 19
targetSdkVersion 33
versionCode = 4800
versionName = "4.8.0"
versionCode = 4900
versionName = "4.9.0"
}
buildTypes {

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

@ -18,11 +18,7 @@ import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.unity3d.ads.IUnityAdsInitializationListener;
import com.unity3d.ads.IUnityAdsLoadListener;
import com.unity3d.ads.IUnityAdsShowListener;
import com.unity3d.ads.UnityAds;
import com.unity3d.ads.UnityAdsShowOptions;
import com.unity3d.ads.*;
import com.unity3d.ads.example.R;
import com.unity3d.ads.metadata.MediationMetaData;
import com.unity3d.ads.metadata.MetaData;
@ -68,6 +64,7 @@ public class UnityAdsFragment extends Fragment {
@Override
public void onBannerFailedToLoad(BannerView bannerAdView, BannerErrorInfo errorInfo) {
Log.e(LOGTAG, "Unity Ads failed to load banner for " + bannerAdView.getPlacementId() + " with error: [" + errorInfo.errorCode + "] " + errorInfo.errorMessage);
enableButton(showBannerButton);
}
@Override
@ -203,6 +200,7 @@ public class UnityAdsFragment extends Fragment {
this.showBannerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
disableButton(showBannerButton);
bottomBanner = new BannerView((Activity)v.getContext(), "bannerads", new UnityBannerSize(320, 50));
bottomBanner.setListener(bannerListener);
bottomBanner.load();
@ -213,11 +211,12 @@ public class UnityAdsFragment extends Fragment {
hideBannerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bottomBanner.removeAllViews();
if (bottomBanner != null) {
bottomBanner.removeAllViews();
bottomBanner = null;
}
showBannerButton.setEnabled(true);
enableButton(showBannerButton);
disableButton(hideBannerButton);
}
});

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

@ -30,8 +30,6 @@ project.tasks.whenTaskAdded { Task theTask ->
}
if (tasksDependentOnScar("${buildType}").any { theTask.name == it }) {
theTask.dependsOn(":unity-scaradapter-common:copyJars${buildType}")
theTask.dependsOn(":unity-scaradapter-1920:copyJars${buildType}")
theTask.dependsOn(":unity-scaradapter-1950:copyJars${buildType}")
theTask.dependsOn(":unity-scaradapter-2000:copyJars${buildType}")
theTask.dependsOn(":unity-scaradapter-2100:copyJars${buildType}")
}

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

@ -1,5 +1,3 @@
include ':unity-scaradapter-1920'
include ':unity-scaradapter-1950'
include ':unity-scaradapter-2000'
include ':unity-scaradapter-2100'
include ':unity-scaradapter-common'

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

@ -13,14 +13,15 @@ if (project.rootProject.file('local.properties').exists()) {
ext {
GROUP_ID = "com.unity3d.ads"
ARTIFACT_ID = "unity-ads"
VERSION_ID = "4.8.0"
VERSION_CODE = 4800
VERSION_ID = "4.9.0"
VERSION_CODE = 4900
SIGN_AAR = properties.getProperty("SIGN_AAR") ?: false
}
android {
namespace = GROUP_ID
compileSdk 33
compileSdkExtension 5
DdmPreferences.setLogLevel("verbose")
DdmPreferences.setTimeOut(10 * 60000)
@ -82,8 +83,6 @@ dependencies {
implementation "com.google.android.gms:play-services-cronet:$play_services_cronet"
androidTestCompileOnly project(':unity-scaradapter-2100')
androidTestCompileOnly project(':unity-scaradapter-2000')
androidTestCompileOnly project(':unity-scaradapter-1950')
androidTestCompileOnly project(':unity-scaradapter-1920')
androidTestCompileOnly project(':unity-scaradapter-common')
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation 'org.mockito:mockito-core:2.28.2'
@ -98,8 +97,6 @@ dependencies {
compileOnly 'com.google.ar:core:1.0.0'
compileOnly project(':unity-scaradapter-2100')
compileOnly project(':unity-scaradapter-2000')
compileOnly project(':unity-scaradapter-1950')
compileOnly project(':unity-scaradapter-1920')
compileOnly project(':unity-scaradapter-common')
testImplementation 'junit:junit:4.13.2'
testImplementation "org.jetbrains.kotlin:kotlin-stdlib"

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

@ -10,10 +10,13 @@ import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.Activity;
import android.content.Context;
import android.widget.RelativeLayout;
import com.unity3d.scar.adapter.common.IScarAdapter;
import com.unity3d.scar.adapter.common.IScarBannerAdListenerWrapper;
import com.unity3d.scar.adapter.common.IScarInterstitialAdListenerWrapper;
import com.unity3d.scar.adapter.common.IScarRewardedAdListenerWrapper;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.scar.adapter.common.scarads.UnityAdFormat;
import com.unity3d.scar.adapter.common.signals.ISignalCollectionListener;
import com.unity3d.services.ads.gmascar.GMAScarAdapterBridge;
import com.unity3d.services.ads.gmascar.adapters.ScarAdapterFactory;
@ -64,19 +67,19 @@ public class GMAScarAdapterBridgeTest {
private static final ScarAdapterVersion VERSION = ScarAdapterVersion.V21;
private GMAScarAdapterBridge gmaScarAdapterBridge;
private IScarAdapter scarAdapter;
private boolean isBannerSignalEnabled = true;
@Before
public void setup() {
scarAdapter = new IScarAdapter() {
@Override
public void getSCARSignals(Context context, String[] strings,
String[] strings1,
ISignalCollectionListener iSignalCollectionListener) {
public void getSCARSignal(Context context, String placementId, UnityAdFormat adFormat, ISignalCollectionListener signalCompletionListener) {
}
@Override
public void getSCARBiddingSignals(Context context,
boolean isBannerEnabled,
ISignalCollectionListener iSignalCollectionListener) {
iSignalCollectionListener.onSignalsCollected(SIGNAL);
}
@ -95,6 +98,11 @@ public class GMAScarAdapterBridgeTest {
}
@Override
public void loadBannerAd(Context context, RelativeLayout bannerView, ScarAdMetadata scarAdMetadata, int width, int height, IScarBannerAdListenerWrapper adListener) {
}
@Override
public void show(Activity activity, String s, String s1) {
@ -189,7 +197,7 @@ public class GMAScarAdapterBridgeTest {
gmaEventSenderMock
);
gmaScarAdapterBridge.getSCARBiddingSignals(handler);
gmaScarAdapterBridge.getSCARBiddingSignals(isBannerSignalEnabled, handler);
verify(handler, times(0)).onSignalsCollectionFailed(anyString());
verify(handler, times(1)).onSignalsCollected(SIGNAL);
}
@ -208,7 +216,7 @@ public class GMAScarAdapterBridgeTest {
gmaEventSenderMock
);
gmaScarAdapterBridge.getSCARBiddingSignals(handler);
gmaScarAdapterBridge.getSCARBiddingSignals(isBannerSignalEnabled, handler);
verify(handler, times(0)).onSignalsCollected(anyString());
verify(handler, times(1)).onSignalsCollectionFailed("SCAR bidding unsupported.");
}
@ -228,7 +236,7 @@ public class GMAScarAdapterBridgeTest {
gmaEventSenderMock
);
gmaScarAdapterBridge.getSCARBiddingSignals(handler);
gmaScarAdapterBridge.getSCARBiddingSignals(isBannerSignalEnabled, handler);
verify(handler, times(0)).onSignalsCollected(SIGNAL);
verify(handler, times(1)).onSignalsCollectionFailed("Could not create SCAR adapter object.");
}

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

@ -19,18 +19,6 @@ public class ScarAdapterFactoryTest {
private final ScarAdapterFactory _scarAdapterFactory = new ScarAdapterFactory();
@Test
public void testScarAdapterFactory1920() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(ScarAdapterVersion.V192, adsErrorHandlerMock);
Assert.assertTrue(adapter instanceof com.unity3d.scar.adapter.v1920.ScarAdapter);
}
@Test
public void testScarAdapterFactory1950() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(ScarAdapterVersion.V195, adsErrorHandlerMock);
Assert.assertTrue(adapter instanceof com.unity3d.scar.adapter.v1950.ScarAdapter);
}
@Test
public void testScarAdapterFactory2000() {
IScarAdapter adapter = _scarAdapterFactory.createScarAdapter(ScarAdapterVersion.V20, adsErrorHandlerMock);

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

@ -34,27 +34,6 @@ public class MobileAdsBridgeLegacyTest {
Assert.assertTrue(String.format("Minor version 223712000 is not found in %s", versionString), versionString.contains("223712000"));
}
@Test
public void testGetAdapterVersionWhen192() {
MobileAdsBridgeLegacy mobileAdsBridge = new MobileAdsBridgeLegacy();
ScarAdapterVersion adapterVersion = mobileAdsBridge.getAdapterVersion(MobileAdsBridgeLegacy.CODE_19_2);
Assert.assertEquals(ScarAdapterVersion.V192, adapterVersion);
}
@Test
public void testGetAdapterVersionWhen195() {
MobileAdsBridgeLegacy mobileAdsBridge = new MobileAdsBridgeLegacy();
ScarAdapterVersion adapterVersion = mobileAdsBridge.getAdapterVersion(MobileAdsBridgeLegacy.CODE_19_5);
Assert.assertEquals(ScarAdapterVersion.V195, adapterVersion);
}
@Test
public void testGetAdapterVersionWhen198() {
MobileAdsBridgeLegacy mobileAdsBridge = new MobileAdsBridgeLegacy();
ScarAdapterVersion adapterVersion = mobileAdsBridge.getAdapterVersion(MobileAdsBridgeLegacy.CODE_19_8);
Assert.assertEquals(ScarAdapterVersion.V195, adapterVersion);
}
@Test
public void testGetAdapterVersionWhen20() {
MobileAdsBridgeLegacy mobileAdsBridge = new MobileAdsBridgeLegacy();

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

@ -38,10 +38,10 @@ public class ScarVersionFinderTest {
public void testScarVersionFinderWithMobileAdsBridgeLegacy() {
doCallRealMethod().when(mobileAdsBridgeMock).getVersionCodeIndex();
Mockito.when(mobileAdsBridgeMock.getVersionString()).thenReturn("afma-sdk-a-v204890999.203404000.1");
Mockito.when(mobileAdsBridgeMock.getVersionString()).thenReturn("afma-sdk-a-v204890999.210402000.1");
ScarVersionFinder scarVersionFinder = new ScarVersionFinder(mobileAdsBridgeMock, presenceDetector, gmaInitializer, new GMAEventSender());
long versionCode = scarVersionFinder.getVersionCode();
Assert.assertEquals(MobileAdsBridgeLegacy.CODE_19_5, versionCode);
Assert.assertEquals(MobileAdsBridgeLegacy.CODE_20_0, versionCode);
}
@Test

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

@ -22,12 +22,13 @@ public class BiddingSignalsHandlerTest {
@Test
public void testSignalsAreSetAfterOnSignalsCollectedWithValidMap() {
String map = "{\"gmaScarBiddingRewardedSignal\": \"rvSig\",\"gmaScarBiddingInterstitialSignal\": \"inSig\"}";
String map = "{\"gmaScarBiddingRewardedSignal\": \"rvSig\",\"gmaScarBiddingInterstitialSignal\": \"inSig\", \"gmaScarBiddingBannerSignal\": \"bnSig\"}";
gmaScarBiddingSignalsListener = spy(new IBiddingSignalsListener() {
@Override
public void onSignalsReady(BiddingSignals signals) {
assertEquals("rvSig", signals.getRvSignal());
assertEquals("inSig", signals.getInterstitialSignal());
assertEquals("bnSig", signals.getBannerSignal());
}
@Override
@ -35,7 +36,7 @@ public class BiddingSignalsHandlerTest {
fail();
}
});
biddingSignalsHandler = new BiddingSignalsHandler(gmaScarBiddingSignalsListener);
biddingSignalsHandler = new BiddingSignalsHandler(true, gmaScarBiddingSignalsListener);
biddingSignalsHandler.onSignalsCollected(map);
verify(gmaScarBiddingSignalsListener, times(1)).onSignalsReady(
@ -44,13 +45,14 @@ public class BiddingSignalsHandlerTest {
@Test
public void testNoInterstitialSignalSetAfterOnSignalsCollectedWithMissingInterstitial() {
String map = "{\"gmaScarBiddingRewardedSignal\": \"rvSig\",\"asdfasdfasf\": \"inSig\"}";
String map = "{\"gmaScarBiddingRewardedSignal\": \"rvSig\", \"gmaScarBiddingBannerSignal\": \"bnSig\"}";
gmaScarBiddingSignalsListener = spy(new IBiddingSignalsListener() {
@Override
public void onSignalsReady(BiddingSignals signals) {
assertEquals("rvSig", signals.getRvSignal());
assertEquals("", signals.getInterstitialSignal());
assertEquals("bnSig", signals.getBannerSignal());
}
@Override
@ -58,7 +60,55 @@ public class BiddingSignalsHandlerTest {
fail();
}
});
biddingSignalsHandler = new BiddingSignalsHandler(gmaScarBiddingSignalsListener);
biddingSignalsHandler = new BiddingSignalsHandler(true, gmaScarBiddingSignalsListener);
biddingSignalsHandler.onSignalsCollected(map);
verify(gmaScarBiddingSignalsListener, times(1)).onSignalsReady(
isA(BiddingSignals.class));
}
@Test
public void testNoBannerSignalSetAfterOnSignalsCollectedWithBannerDisabled() {
String map = "{\"gmaScarBiddingRewardedSignal\": \"rvSig\",\"gmaScarBiddingInterstitialSignal\": \"inSig\"}";
gmaScarBiddingSignalsListener = spy(new IBiddingSignalsListener() {
@Override
public void onSignalsReady(BiddingSignals signals) {
assertEquals("rvSig", signals.getRvSignal());
assertEquals("inSig", signals.getInterstitialSignal());
assertEquals("", signals.getBannerSignal());
}
@Override
public void onSignalsFailure(String msg) {
fail();
}
});
biddingSignalsHandler = new BiddingSignalsHandler(false, gmaScarBiddingSignalsListener);
biddingSignalsHandler.onSignalsCollected(map);
verify(gmaScarBiddingSignalsListener, times(1)).onSignalsReady(
isA(BiddingSignals.class));
}
@Test
public void testNoBannerSignalSetAfterOnSignalsCollectedWithBannerPresent() {
String map = "{\"gmaScarBiddingRewardedSignal\": \"rvSig\",\"gmaScarBiddingInterstitialSignal\": \"inSig\", \"gmaScarBiddingBannerSignal\": \"bnSig\"}";
gmaScarBiddingSignalsListener = spy(new IBiddingSignalsListener() {
@Override
public void onSignalsReady(BiddingSignals signals) {
assertEquals("rvSig", signals.getRvSignal());
assertEquals("inSig", signals.getInterstitialSignal());
assertEquals("", signals.getBannerSignal());
}
@Override
public void onSignalsFailure(String msg) {
fail();
}
});
biddingSignalsHandler = new BiddingSignalsHandler(false, gmaScarBiddingSignalsListener);
biddingSignalsHandler.onSignalsCollected(map);
verify(gmaScarBiddingSignalsListener, times(1)).onSignalsReady(
@ -80,7 +130,7 @@ public class BiddingSignalsHandlerTest {
fail();
}
});
biddingSignalsHandler = new BiddingSignalsHandler(gmaScarBiddingSignalsListener);
biddingSignalsHandler = new BiddingSignalsHandler(false, gmaScarBiddingSignalsListener);
biddingSignalsHandler.onSignalsCollected(map);
verify(gmaScarBiddingSignalsListener, times(1)).onSignalsReady(

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

@ -0,0 +1,101 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.handlers;
import com.unity3d.services.ads.gmascar.handlers.ScarBannerAdHandler;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
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;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
@RunWith(MockitoJUnitRunner.class)
public class ScarBannerAdHandlerTest {
@Mock
WebViewApp mockWebViewApp;
private String testBannerId = "test-bannerId";
@Before
public void setup() {
WebViewApp.setCurrentApp(mockWebViewApp);
}
@Test
public void testOnAdLoaded() {
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(testBannerId);
adHandler.onAdLoaded();
Mockito.verify(mockWebViewApp, times(1)).sendEvent(eq(WebViewEventCategory.BANNER), eq(BannerBridge.BannerEvent.SCAR_BANNER_LOADED), eq(testBannerId));
}
@Test
public void testOnAdFailedToLoad() {
int errorCode = 123;
String errorString = "Test Error Message";
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(testBannerId);
adHandler.onAdFailedToLoad(errorCode, errorString);
Mockito.verify(mockWebViewApp, times(1)).sendEvent(
eq(WebViewEventCategory.BANNER),
eq(BannerBridge.BannerEvent.SCAR_BANNER_LOAD_FAILED),
eq(testBannerId)
);
}
@Test
public void testOnAdOpened() {
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(testBannerId);
adHandler.onAdOpened();
Mockito.verify(mockWebViewApp, times(1)).sendEvent(
eq(WebViewEventCategory.BANNER),
eq(BannerBridge.BannerEvent.SCAR_BANNER_OPENED),
eq(testBannerId)
);
}
@Test
public void testOnAdClicked() {
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(testBannerId);
adHandler.onAdClicked();
Mockito.verify(mockWebViewApp, times(1)).sendEvent(
eq(WebViewEventCategory.BANNER),
eq(BannerBridge.BannerEvent.SCAR_BANNER_CLICKED),
eq(testBannerId)
);
}
@Test
public void testOnAdClosed() {
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(testBannerId);
adHandler.onAdClosed();
Mockito.verify(mockWebViewApp, times(1)).sendEvent(
eq(WebViewEventCategory.BANNER),
eq(BannerBridge.BannerEvent.SCAR_BANNER_CLOSED),
eq(testBannerId)
);
}
@Test
public void testOnAdImpression() {
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(testBannerId);
adHandler.onAdImpression();
Mockito.verify(mockWebViewApp, times(1)).sendEvent(
eq(WebViewEventCategory.BANNER),
eq(BannerBridge.BannerEvent.SCAR_BANNER_IMPRESSION),
eq(testBannerId)
);
}
}

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

@ -38,7 +38,7 @@ public class BiddingBaseManagerTest {
private BiddingBaseManager managerWithTokenListener;
private BiddingBaseManager managerWithNullListener;
private boolean isBannerEnabled = true;
private String TEST_TOKEN = "token";
private boolean isAsyncTokenCall = true;
private boolean isNotAsyncTokenCall = false;
@ -48,28 +48,18 @@ public class BiddingBaseManagerTest {
public void setup() {
ClientProperties.setApplication((Application) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext());
ClientProperties.setApplicationContext(InstrumentationRegistry.getInstrumentation().getTargetContext());
managerWithTokenListener = Mockito.spy(new BiddingBaseManager(publisherListener) {
managerWithTokenListener = Mockito.spy(new BiddingBaseManager(isBannerEnabled, publisherListener) {
@Override
public void start() {
}
@Override
public void onUnityTokenSuccessfullyFetched() {
}
});
managerWithNullListener = Mockito.spy(new BiddingBaseManager(null) {
managerWithNullListener = Mockito.spy(new BiddingBaseManager(isBannerEnabled, null) {
@Override
public void start() {
}
@Override
public void onUnityTokenSuccessfullyFetched() {
}
});
Mockito.when(managerWithTokenListener.getMetricSender()).thenReturn(_metricSenderMock);
@ -115,16 +105,11 @@ public class BiddingBaseManagerTest {
@Test
public void testSendsAsyncMetricWhenUploadStartsAndSucceedsWithAsyncTokenCall() throws InterruptedException {
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(publisherListener, _scarRequestHandlerMock) {
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(isBannerEnabled, publisherListener, _scarRequestHandlerMock) {
@Override
public void start() {
}
@Override
public void onUnityTokenSuccessfullyFetched() {
}
});
Mockito.when(managerWithScarRequestSender.getMetricSender()).thenReturn(_metricSenderMock);
@ -133,7 +118,7 @@ public class BiddingBaseManagerTest {
final Metric secondDesiredMetric = ScarMetric.hbSignalsUploadSuccess(isAsyncTokenCall);
managerWithScarRequestSender.permitUpload();
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal"));
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal", "testBannerSignal"));
Thread.sleep(1000);
@ -170,21 +155,16 @@ public class BiddingBaseManagerTest {
public void testSendsAsyncMetricWhenUploadRequestFailsWithMalformedUrlAndAsyncTokenCall() throws Exception {
String errorMessage = "bad request";
Mockito.doThrow(new Exception(errorMessage)).when(_scarRequestHandlerMock).makeUploadRequest(Mockito.<String>any(), Mockito.<BiddingSignals>any(), any(String.class));
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(publisherListener, _scarRequestHandlerMock) {
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(isBannerEnabled, publisherListener, _scarRequestHandlerMock) {
@Override
public void start() {
}
@Override
public void onUnityTokenSuccessfullyFetched() {
}
});
Mockito.when(managerWithScarRequestSender.getMetricSender()).thenReturn(_metricSenderMock);
managerWithScarRequestSender.permitUpload();
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal"));
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal", "testBannerSignal"));
final ArgumentCaptor<Metric> metricsCaptor = ArgumentCaptor.forClass(Metric.class);
final Metric firstDesiredMetric = ScarMetric.hbSignalsUploadStart(isAsyncTokenCall);
@ -242,16 +222,11 @@ public class BiddingBaseManagerTest {
@Test
public void testSendsSyncMetricWhenUploadStartsAndSucceedsWithSyncTokenCall() throws InterruptedException {
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(null, _scarRequestHandlerMock) {
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(isBannerEnabled, null, _scarRequestHandlerMock) {
@Override
public void start() {
}
@Override
public void onUnityTokenSuccessfullyFetched() {
}
});
Mockito.when(managerWithScarRequestSender.getMetricSender()).thenReturn(_metricSenderMock);
@ -260,7 +235,7 @@ public class BiddingBaseManagerTest {
final Metric secondDesiredMetric = ScarMetric.hbSignalsUploadSuccess(isNotAsyncTokenCall);
managerWithScarRequestSender.permitUpload();
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal"));
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal", "testBannerSignal"));
Thread.sleep(1000);
@ -297,21 +272,16 @@ public class BiddingBaseManagerTest {
public void testSendsSyncMetricWhenUploadRequestFailsWithMalformedUrlAndSyncTokenCall() throws Exception {
String errorMessage = "bad request";
Mockito.doThrow(new Exception(errorMessage)).when(_scarRequestHandlerMock).makeUploadRequest(Mockito.<String>any(), Mockito.<BiddingSignals>any(), any(String.class));
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(null, _scarRequestHandlerMock) {
BiddingBaseManager managerWithScarRequestSender = Mockito.spy(new BiddingBaseManager(isBannerEnabled, null, _scarRequestHandlerMock) {
@Override
public void start() {
}
@Override
public void onUnityTokenSuccessfullyFetched() {
}
});
Mockito.when(managerWithScarRequestSender.getMetricSender()).thenReturn(_metricSenderMock);
managerWithScarRequestSender.permitUpload();
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal"));
managerWithScarRequestSender.onSignalsReady(new BiddingSignals("testRewardedSignal", "testInterstitialSignal", "testBannerSignal"));
final ArgumentCaptor<Metric> metricsCaptor = ArgumentCaptor.forClass(Metric.class);
final Metric firstDesiredMetric = ScarMetric.hbSignalsUploadStart(isNotAsyncTokenCall);
@ -336,29 +306,11 @@ public class BiddingBaseManagerTest {
Mockito.verify(publisherListener, Mockito.times(1)).onUnityAdsTokenReady(TEST_TOKEN);
}
@Test
public void testOnUnityTokenSuccessfullyFetchedAfterOnUnityAdsTokenReadyWithValidToken() {
managerWithTokenListener.onUnityAdsTokenReady(TEST_TOKEN);
Mockito.verify(managerWithTokenListener, Mockito.times(1)).onUnityTokenSuccessfullyFetched();
}
@Test
public void testOnUnityTokenSuccessfullyFetchedAfterOnUnityAdsTokenReadyWithNullToken() {
managerWithTokenListener.onUnityAdsTokenReady(null);
Mockito.verify(managerWithTokenListener, Mockito.times(0)).onUnityTokenSuccessfullyFetched();
}
@Test
public void testOnUnityTokenSuccessfullyFetchedAfterOnUnityAdsTokenReadyWithEmptyToken() {
managerWithTokenListener.onUnityAdsTokenReady("");
Mockito.verify(managerWithTokenListener, Mockito.times(0)).onUnityTokenSuccessfullyFetched();
}
@Test
public void testUploadSignalsAfterPermittedAndSignalsReady() throws InterruptedException {
managerWithTokenListener.permitSignalsUpload();
Thread.sleep(100);
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test", "test"));
Mockito.verify(managerWithTokenListener, Mockito.times(1)).uploadSignals();
}
@ -371,7 +323,7 @@ public class BiddingBaseManagerTest {
@Test
public void testUploadSignalsAfterSignalsReadyButNotPermitted() throws InterruptedException {
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test", "test"));
Thread.sleep(100);
Mockito.verify(managerWithTokenListener, Mockito.times(0)).uploadSignals();
}
@ -381,9 +333,9 @@ public class BiddingBaseManagerTest {
managerWithTokenListener.permitSignalsUpload();
Thread.sleep(100);
Mockito.verify(managerWithTokenListener, Mockito.times(0)).uploadSignals();
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test", "test"));
managerWithTokenListener.onSignalsReady(new BiddingSignals("test", "test", "test"));
Mockito.verify(managerWithTokenListener, Mockito.times(1)).uploadSignals();
}
}

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

@ -17,10 +17,11 @@ public class BiddingEagerManagerTest {
IUnityAdsTokenListener callerListener;
private BiddingEagerManager manager;
private String TEST_TOKEN = "token";
private boolean isBannerEnabled = true;
@Before
public void setup() {
manager = Mockito.spy(new BiddingEagerManager(callerListener));
manager = Mockito.spy(new BiddingEagerManager(isBannerEnabled, callerListener));
}
@Test
@ -30,13 +31,6 @@ public class BiddingEagerManagerTest {
Mockito.verify(manager, Mockito.times(1)).permitSignalsUpload();
}
@Test
public void testNoFetchAndUploadSignalsAfterOnUnityTokenSuccessfullyFetched() {
manager.onUnityTokenSuccessfullyFetched();
Mockito.verify(manager, Mockito.times(0)).fetchSignals();
Mockito.verify(manager, Mockito.times(0)).permitSignalsUpload();
}
@Test
public void testListenerIsInvokedOnUnityAdsTokenReady() {
manager.onUnityAdsTokenReady(TEST_TOKEN);

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

@ -1,46 +0,0 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.managers;
import com.unity3d.ads.IUnityAdsTokenListener;
import com.unity3d.services.ads.gmascar.managers.BiddingLazyManager;
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 BiddingLazyManagerTest {
@Mock
IUnityAdsTokenListener callerListener;
private BiddingLazyManager manager;
private String TEST_TOKEN = "token";
@Before
public void setup() {
manager = Mockito.spy(new BiddingLazyManager(callerListener));
}
@Test
public void testNoActionAfterStartCalled() {
manager.start();
Mockito.verify(manager, Mockito.times(0)).fetchSignals();
Mockito.verify(manager, Mockito.times(0)).permitSignalsUpload();
}
@Test
public void testPermitAndFetchSignalsAfterStartCalled() {
manager.onUnityTokenSuccessfullyFetched();
Mockito.verify(manager, Mockito.times(1)).fetchSignals();
Mockito.verify(manager, Mockito.times(1)).permitSignalsUpload();
}
@Test
public void testListenerIsInvokedOnUnityAdsTokenReady() {
manager.onUnityAdsTokenReady(TEST_TOKEN);
Mockito.verify(callerListener, Mockito.times(1)).onUnityAdsTokenReady(TEST_TOKEN);
}
}

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

@ -1,45 +0,0 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.managers;
import com.unity3d.ads.IUnityAdsTokenListener;
import com.unity3d.services.ads.gmascar.managers.BiddingOnDemandManager;
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 BiddingOnDemandManagerTest {
@Mock
IUnityAdsTokenListener callerListener;
private BiddingOnDemandManager manager;
private String TEST_TOKEN = "token";
@Before
public void setup() {
manager = Mockito.spy(new BiddingOnDemandManager(callerListener));
}
@Test
public void testOnlyFetchSignalsCalledAfterStartCalled() {
manager.start();
Mockito.verify(manager, Mockito.times(1)).fetchSignals();
Mockito.verify(manager, Mockito.times(0)).permitSignalsUpload();
}
@Test
public void testOnlyPermitSignalsCalledAfterOnUnityTokenSuccessfullyFetched() {
manager.onUnityTokenSuccessfullyFetched();
Mockito.verify(manager, Mockito.times(0)).fetchSignals();
Mockito.verify(manager, Mockito.times(1)).permitSignalsUpload();
}
@Test
public void testListenerIsInvokedOnUnityAdsTokenReady() {
manager.onUnityAdsTokenReady(TEST_TOKEN);
Mockito.verify(callerListener, Mockito.times(1)).onUnityAdsTokenReady(TEST_TOKEN);
}
}

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

@ -1,26 +0,0 @@
package com.unity3d.ads.test.instrumentation.services.ads.gmascar.managers;
import static org.junit.Assert.assertEquals;
import com.unity3d.services.ads.gmascar.managers.SCARBiddingManagerType;
import org.junit.Test;
public class ScarBiddingManagerEnumTest {
@Test
public void testEnumToNameMappings() {
assertEquals("dis", SCARBiddingManagerType.DISABLED.getName());
assertEquals("hyb", SCARBiddingManagerType.HYBRID.getName());
assertEquals("laz", SCARBiddingManagerType.LAZY.getName());
assertEquals("eag", SCARBiddingManagerType.EAGER.getName());
}
@Test
public void testNameToEnumMappings() {
assertEquals(SCARBiddingManagerType.DISABLED, SCARBiddingManagerType.fromName("dis"));
assertEquals(SCARBiddingManagerType.HYBRID, SCARBiddingManagerType.fromName("hyb"));
assertEquals(SCARBiddingManagerType.LAZY, SCARBiddingManagerType.fromName("laz"));
assertEquals(SCARBiddingManagerType.EAGER, SCARBiddingManagerType.fromName("eag"));
}
}

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

@ -13,11 +13,12 @@ import com.unity3d.ads.test.TestUtilities;
import com.unity3d.services.ads.operation.load.ILoadModule;
import com.unity3d.services.ads.operation.load.LoadModuleDecoratorTimeout;
import com.unity3d.services.ads.operation.load.LoadOperationState;
import com.unity3d.services.core.configuration.Configuration;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.ExperimentObjects;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@ -35,23 +36,23 @@ public class LoadModuleDecoratorTimeoutTests {
private IUnityAdsLoadListener loadListenerMock;
private ILoadModule loadModuleMock;
private SDKMetricsSender sdkMetricsMock;
private ConfigurationReader configurationReaderMock;
private ExperimentsReader experimentsReaderMock;
@Before
public void beforeEachTest() {
loadListenerMock = mock(IUnityAdsLoadListener.class);
loadModuleMock = mock(ILoadModule.class);
sdkMetricsMock = mock(SDKMetricsSender.class);
configurationReaderMock = mock(ConfigurationReader.class);
experimentsReaderMock = mock(ExperimentsReader.class);
Mockito.when(configurationReaderMock.getCurrentConfiguration()).thenReturn(new Configuration());
Mockito.when(experimentsReaderMock.getCurrentlyActiveExperiments()).thenReturn(new ExperimentObjects(new JSONObject()));
}
@Test
public void onUnityAdsFailedToLoadIsCalledWhenTimeoutIsReached() {
Mockito.when(loadModuleMock.getMetricSender()).thenReturn(sdkMetricsMock);
LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout));
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, configurationReaderMock);
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, experimentsReaderMock);
timeoutDecorator.executeAdOperation(mock(IWebViewBridgeInvoker.class), loadOperationState);
Mockito.verify(loadListenerMock, timeout(maxWaitTime).times(1)).onUnityAdsFailedToLoad(placementId, UnityAds.UnityAdsLoadError.TIMEOUT, "[UnityAds] Timeout while loading " + placementId);
@ -61,7 +62,7 @@ public class LoadModuleDecoratorTimeoutTests {
public void onUnityAdsAdLoadedAndOnUnityAdsAdFailedToLoadIsNotCalledAgainWhenTimeoutHasBeenReached() {
Mockito.when(loadModuleMock.getMetricSender()).thenReturn(sdkMetricsMock);
LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout));
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, configurationReaderMock);
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, experimentsReaderMock);
timeoutDecorator.executeAdOperation(mock(IWebViewBridgeInvoker.class), loadOperationState);
TestUtilities.SleepCurrentThread(loadTimeoutExpireMs);
@ -76,7 +77,7 @@ public class LoadModuleDecoratorTimeoutTests {
public void onUnityAdsAdFailedToLoadIsNotCalledWhenOnUnityAdsAdLoadedIsCalledBeforeTimeout() {
Mockito.when(loadModuleMock.getMetricSender()).thenReturn(sdkMetricsMock);
LoadOperationState loadOperationState = new LoadOperationState(placementId, loadListenerMock, loadOptions, OperationTestUtilities.createConfigurationWithLoadTimeout(loadTimeout));
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, configurationReaderMock);
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, experimentsReaderMock);
timeoutDecorator.executeAdOperation(mock(IWebViewBridgeInvoker.class), loadOperationState);
TestUtilities.SleepCurrentThread(25);
@ -88,14 +89,14 @@ public class LoadModuleDecoratorTimeoutTests {
@Test
public void noNPEIsThrownWhenOnUnityAdsAdLoadedIsCalledWithoutCallingExecuteAdOperation() {
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, configurationReaderMock);
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, experimentsReaderMock);
timeoutDecorator.onUnityAdsAdLoaded(placementId);
Mockito.verify(loadModuleMock, times(0)).onUnityAdsFailedToLoad(anyString(), any(UnityAds.UnityAdsLoadError.class), anyString());
}
@Test
public void noNPEIsThrownWhenOnUnityAdsAdFailedToLoadIsCalledWithoutCallingExecuteAdOperation() {
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, configurationReaderMock);
LoadModuleDecoratorTimeout timeoutDecorator = new LoadModuleDecoratorTimeout(loadModuleMock, experimentsReaderMock);
timeoutDecorator.onUnityAdsFailedToLoad(placementId, loadError, loadErrorMessage);
Mockito.verify(loadModuleMock, times(0)).onUnityAdsAdLoaded(anyString());
}

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

@ -17,8 +17,8 @@ import com.unity3d.services.ads.operation.show.IShowModule;
import com.unity3d.services.ads.operation.show.ShowModule;
import com.unity3d.services.ads.operation.show.ShowModuleDecoratorTimeout;
import com.unity3d.services.ads.operation.show.ShowOperationState;
import com.unity3d.services.core.configuration.Configuration;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.Experiments;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.webview.bridge.CallbackStatus;
import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker;
@ -41,7 +41,7 @@ public class ShowModuleDecoratorTimeoutTests {
private IUnityAdsShowListener showListenerMock;
private IShowModule showModule;
private SDKMetricsSender sdkMetricsMock;
private ConfigurationReader configurationReaderMock;
private ExperimentsReader experimentsReaderMock;
@Rule
public final ActivityTestRule<InstrumentationTestActivity> _activityRule = new ActivityTestRule<>(InstrumentationTestActivity.class);
@ -50,16 +50,16 @@ public class ShowModuleDecoratorTimeoutTests {
public void beforeEachTest() {
showListenerMock = mock(IUnityAdsShowListener.class);
sdkMetricsMock = mock(SDKMetricsSender.class);
configurationReaderMock = mock(ConfigurationReader.class);
experimentsReaderMock = mock(ExperimentsReader.class);
// We need a real instance since ShowModule will create the Operation object (which holds the State with Listener ID)
showModule = new ShowModule(sdkMetricsMock);
Mockito.when(configurationReaderMock.getCurrentConfiguration()).thenReturn(new Configuration());
Mockito.when(experimentsReaderMock.getCurrentlyActiveExperiments()).thenReturn(new Experiments());
}
@Test
public void testShowModuleDecoratorTimeout() {
ShowModuleDecoratorTimeout showModuleDecoratorTimeout = new ShowModuleDecoratorTimeout(showModule, configurationReaderMock);
ShowModuleDecoratorTimeout showModuleDecoratorTimeout = new ShowModuleDecoratorTimeout(showModule, experimentsReaderMock);
ShowOperationState showOperationState = new ShowOperationState(placementId, showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout));
showModuleDecoratorTimeout.executeAdOperation(mock(IWebViewBridgeInvoker.class), showOperationState);
@ -68,7 +68,7 @@ public class ShowModuleDecoratorTimeoutTests {
@Test
public void testShowModuleDecoratorShowConsentNoTimeout() {
ShowModuleDecoratorTimeout showModuleDecoratorTimeout = new ShowModuleDecoratorTimeout(showModule, configurationReaderMock);
ShowModuleDecoratorTimeout showModuleDecoratorTimeout = new ShowModuleDecoratorTimeout(showModule, experimentsReaderMock);
ShowOperationState showOperationState = new ShowOperationState(placementId, showListenerMock, _activityRule.getActivity(), showOptions, OperationTestUtilities.createConfigurationWithShowTimeout(showTimeout));
IWebViewBridgeInvoker webViewBridgeInvoker = mock(IWebViewBridgeInvoker.class);
when(webViewBridgeInvoker.invokeMethod(anyString(), anyString(), any(Method.class), any())).thenReturn(true);

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

@ -0,0 +1,41 @@
package com.unity3d.ads.test.instrumentation.services.banners;
import com.unity3d.ads.UnityAds;
import com.unity3d.services.banners.BannerErrorCode;
import com.unity3d.services.banners.BannerErrorInfo;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BannerErrorInfoTest {
private String errorMessage = "BANNER ERROR MSG";
@Test
public void testConvertNativeBannerErrorToLoadError() {
BannerErrorInfo nativeError = new BannerErrorInfo(errorMessage, BannerErrorCode.NATIVE_ERROR);
UnityAds.UnityAdsLoadError loadError = nativeError.toLoadError();
assertEquals(UnityAds.UnityAdsLoadError.INVALID_ARGUMENT, loadError);
}
@Test
public void testConvertNoFillBannerErrorToLoadError() {
BannerErrorInfo noFillError = new BannerErrorInfo(errorMessage, BannerErrorCode.NO_FILL);
UnityAds.UnityAdsLoadError loadError = noFillError.toLoadError();
assertEquals(UnityAds.UnityAdsLoadError.NO_FILL, loadError);
}
@Test
public void testConvertWebViewBannerErrorToLoadError() {
BannerErrorInfo webViewError = new BannerErrorInfo(errorMessage, BannerErrorCode.WEBVIEW_ERROR);
UnityAds.UnityAdsLoadError loadError = webViewError.toLoadError();
assertEquals(UnityAds.UnityAdsLoadError.INTERNAL_ERROR, loadError);
}
@Test
public void testConvertUnknownBannerErrorToLoadError() {
BannerErrorInfo unknownError = new BannerErrorInfo(errorMessage, BannerErrorCode.UNKNOWN);
UnityAds.UnityAdsLoadError loadError = unknownError.toLoadError();
assertEquals(UnityAds.UnityAdsLoadError.INTERNAL_ERROR, loadError);
}
}

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

@ -68,18 +68,28 @@ public class BannerViewCacheTests {
}
@Test
public void testTriggerBannerLoadEvent() throws InterruptedException {
public void testTriggerBannerLoadEvent() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
BannerViewCache bannerViewCache = new BannerViewCache();
final BannerView bannerView = new BannerView(_activityRule.getActivity(), "test", new UnityBannerSize(320, 50));
String bannerAdId = bannerViewCache.addBannerView(bannerView);
final Semaphore _loadedSemaphore = new Semaphore(0);
ILoadModule loadBannerModule = new LoadBannerModule(Mockito.mock(SDKMetricsSender.class));
ILoadModule spy = Mockito.spy(loadBannerModule);
Field instance = LoadBannerModule.class.getDeclaredField("_instance");
instance.setAccessible(true);
instance.set(LoadBannerModule.class, spy);
bannerView.setListener(new BannerView.Listener() {
@Override
public void onBannerLoaded(BannerView bannerAdView) {
assertEquals(bannerView, bannerAdView);
Mockito.verify(spy).onUnityAdsAdLoaded(bannerAdId);
_loadedSemaphore.release();
}
});
bannerViewCache.triggerBannerLoadEvent(bannerAdId);
_loadedSemaphore.acquire();
}
@ -119,7 +129,7 @@ public class BannerViewCacheTests {
}
@Test
public void testTriggerBannerErrorEvent() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
public void testTriggerBannerErrorEventWithNativeError() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
BannerViewCache bannerViewCache = new BannerViewCache();
final BannerView bannerView = new BannerView(_activityRule.getActivity(), "test", new UnityBannerSize(320, 50));
final BannerErrorInfo bannerErrorInfo = new BannerErrorInfo("test error", BannerErrorCode.NATIVE_ERROR);
@ -133,6 +143,52 @@ public class BannerViewCacheTests {
ILoadOperation loadOperation = Mockito.mock(ILoadOperation.class);
Mockito.when(loadOperation.getLoadOperationState()).thenReturn(loadOperationState);
ILoadModule loadBannerModule = new LoadBannerModule(Mockito.mock(SDKMetricsSender.class));
ILoadModule spy = Mockito.spy(loadBannerModule);
Mockito.when(spy.get(Mockito.anyString())).thenReturn(loadOperation);
Mockito.doAnswer(invocation -> {
Mockito.doAnswer(invocation1 -> {
LoadOperationState state = invocation.getArgument(1);
state.onUnityAdsFailedToLoad(UnityAds.UnityAdsLoadError.INVALID_ARGUMENT, "test error");
return null;
}).when(loadOperation).onUnityAdsFailedToLoad(Mockito.anyString(), Mockito.any(), Mockito.any());
return null;
}).when(spy).executeAdOperation(Mockito.any(), Mockito.any());
Field instance = LoadBannerModule.class.getDeclaredField("_instance");
instance.setAccessible(true);
instance.set(LoadBannerModule.class, spy);
final Semaphore _errorSemaphore = new Semaphore(0);
bannerView.setListener(new BannerView.Listener() {
@Override
public void onBannerFailedToLoad(BannerView bannerAdView, BannerErrorInfo _bannerErrorInfo) {
assertEquals(bannerView, bannerAdView);
assertEquals(BannerErrorCode.NATIVE_ERROR, _bannerErrorInfo.errorCode);
Mockito.verify(spy).onUnityAdsFailedToLoad(bannerAdId, UnityAds.UnityAdsLoadError.INVALID_ARGUMENT, "test error");
_errorSemaphore.release();
}
});
bannerView.load();
bannerViewCache.triggerBannerErrorEvent(bannerAdId, bannerErrorInfo);
_errorSemaphore.acquire();
}
@Test
public void testTriggerBannerErrorEventWithWebViewError() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
BannerViewCache bannerViewCache = new BannerViewCache();
final BannerView bannerView = new BannerView(_activityRule.getActivity(), "test", new UnityBannerSize(320, 50));
final BannerErrorInfo bannerErrorInfo = new BannerErrorInfo("test error", BannerErrorCode.WEBVIEW_ERROR);
String bannerAdId = bannerViewCache.addBannerView(bannerView);
LoadOperationState loadOperationState = Mockito.mock(LoadOperationState.class);
Mockito.when(loadOperationState.isBanner()).thenReturn(true);
Mockito.when(loadOperationState.duration()).thenReturn(Long.MAX_VALUE);
loadOperationState.placementId = "test";
ILoadOperation loadOperation = Mockito.mock(ILoadOperation.class);
Mockito.when(loadOperation.getLoadOperationState()).thenReturn(loadOperationState);
ILoadModule loadBannerModule = new LoadBannerModule(Mockito.mock(SDKMetricsSender.class));
ILoadModule spy = Mockito.spy(loadBannerModule);
Mockito.when(spy.get(Mockito.anyString())).thenReturn(loadOperation);
@ -154,6 +210,7 @@ public class BannerViewCacheTests {
@Override
public void onBannerFailedToLoad(BannerView bannerAdView, BannerErrorInfo _bannerErrorInfo) {
assertEquals(bannerView, bannerAdView);
assertEquals(BannerErrorCode.WEBVIEW_ERROR, _bannerErrorInfo.errorCode);
Mockito.verify(spy).onUnityAdsFailedToLoad(bannerAdId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, "test error");
_errorSemaphore.release();
}
@ -179,5 +236,4 @@ public class BannerViewCacheTests {
bannerViewCache.triggerBannerLeftApplicationEvent(bannerAdId);
_leftApplicationSemaphore.acquire();
}
}

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

@ -1,12 +1,9 @@
package com.unity3d.ads.test.instrumentation.services.core.webview;
import com.unity3d.services.core.configuration.Configuration;
import com.unity3d.services.core.configuration.Experiments;
import com.unity3d.services.core.request.metrics.SDKMetrics;
import com.unity3d.services.core.webview.WebViewUrlBuilder;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -57,7 +54,7 @@ public class WebViewUrlBuilderTest {
@Test
public void testWebViewUrlBuilderWithMetricsEnabled() {
Mockito.when(_configMock.getExperiments()).thenReturn(null);
Mockito.when(_configMock.getMetricSampleRate()).thenReturn(100.0);
Mockito.when(_configMock.areMetricsEnabledForCurrentSession()).thenReturn(Boolean.TRUE);
Mockito.when(_configMock.getMetricsUrl()).thenReturn(TEST_BASE_URL);
SDKMetrics.setConfiguration(_configMock);
WebViewUrlBuilder webViewUrlBuilder = new WebViewUrlBuilder(TEST_BASE_URL, _configMock);

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

@ -100,31 +100,6 @@ public class SDKMetricsTest {
SDKMetrics.getInstance().sendEvent("test_event");
}
@Test
public void testSettingMsr100Then0() throws NoSuchFieldException, IllegalAccessException {
validateAndTestChangingSampleRate("testUrl", 100.0, 0.0, MetricSender.class);
Assert.assertTrue("Metrics expected to be enabled for session", SDKMetrics.getInstance().areMetricsEnabledForCurrentSession());
}
@Test
public void testSettingMsr0Then100() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class expectedClass = Class.forName("com.unity3d.services.core.request.metrics.SDKMetrics$NullInstance");
validateAndTestChangingSampleRate("testUrl", 0.0, 100.0, expectedClass);
Assert.assertFalse("Metrics expected to be disabled for session", SDKMetrics.getInstance().areMetricsEnabledForCurrentSession());
}
@Test
public void testSettingMsrWithNullUrl() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class expectedClass = Class.forName("com.unity3d.services.core.request.metrics.SDKMetrics$NullInstance");
validateAndTestChangingSampleRate(null, 100.0, 100.0, expectedClass);
}
@Test
public void testSettingMsrWithEmptyUrl() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class expectedClass = Class.forName("com.unity3d.services.core.request.metrics.SDKMetrics$NullInstance");
validateAndTestChangingSampleRate("", 100.0, 100.0, expectedClass);
}
@Test
public void testMalformedUrlFromConfiguration() throws Exception {
JSONObject json = new JSONObject();
@ -159,20 +134,4 @@ public class SDKMetricsTest {
Mockito.verify(_metricSenderMock).sendMetric(metricCapture.capture());
Assert.assertEquals(TEST_STATE_TAGS, metricCapture.getValue().getTags());
}
private void validateAndTestChangingSampleRate(String metricsUrl, double oldMsr, double newMsr, Class expectedMetricsClass) throws NoSuchFieldException, IllegalAccessException {
Configuration mockConfiguration = Mockito.mock(Configuration.class);
Mockito.when(mockConfiguration.getMetricsUrl()).thenReturn(metricsUrl);
Mockito.when(mockConfiguration.getMetricSampleRate()).thenReturn(oldMsr);
SDKMetrics.setConfiguration(mockConfiguration);
SDKMetrics.getInstance();
Field instanceField = SDKMetrics.class.getDeclaredField("_instance");
instanceField.setAccessible(true);
Object instanceFieldObj = instanceField.get(SDKMetricsSender.class);
Assert.assertEquals(expectedMetricsClass, instanceFieldObj.getClass());
Mockito.when(mockConfiguration.getMetricSampleRate()).thenReturn(newMsr);
SDKMetrics.setConfiguration(mockConfiguration);
instanceFieldObj = instanceField.get(SDKMetricsSender.class);
Assert.assertEquals(expectedMetricsClass, instanceFieldObj.getClass());
}
}

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

@ -40,6 +40,11 @@ public class DeviceTest {
assertTrue("Expected SDK_INT should be > 9", Device.getApiLevel() >= 9);
}
@Test
public void testExtensionVersion () {
assertTrue("Expected extension version should be > 4", Device.getExtensionVersion() >= -1);
}
@Test
public void testScreenLayout () {
assertTrue("Expected screenLayout property be something else than undefined (0)", Device.getScreenLayout() > 0);

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

@ -2,7 +2,6 @@ package com.unity3d.services
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.request.metrics.Metric
import com.unity3d.services.core.request.metrics.SDKMetrics
import com.unity3d.services.core.request.metrics.SDKMetricsSender
import kotlinx.coroutines.CoroutineExceptionHandler
import java.lang.IllegalStateException
@ -14,19 +13,19 @@ class SDKErrorHandler(private val dispatchers: ISDKDispatchers, private val sdkM
override val key = CoroutineExceptionHandler.Key
override fun handleException(context: CoroutineContext, exception: Throwable) {
val className: String = exception.stackTrace[0].fileName
val line: Int = exception.stackTrace[0].lineNumber
val className: String = exception.stackTrace[0]?.fileName ?: "unknown"
val line: Int = exception.stackTrace[0]?.lineNumber ?: 0
val name: String = when (exception) {
is NullPointerException -> "native_exception_npe"
is OutOfMemoryError -> "native_exception_oom"
is IllegalStateException -> "native_exception_ise"
is RuntimeException -> "native_exception_re"
is SecurityException -> "native_exception_se"
is RuntimeException -> "native_exception_re"
else -> "native_exception"
}
sendMetric(Metric(name, "{$className}_$line"))
sendMetric(Metric(name, "${className}_$line"))
}

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

@ -27,7 +27,6 @@ import com.unity3d.services.core.log.DeviceLog;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.properties.ClientProperties;
import com.unity3d.services.core.request.metrics.AdOperationMetric;
import com.unity3d.services.core.request.metrics.SDKMetrics;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.bridge.WebViewBridgeInvoker;
@ -203,7 +202,7 @@ public final class UnityAdsImplementation implements IUnityAds {
}
Configuration config = configuration == null ? new ConfigurationReader().getCurrentConfiguration() : configuration;
BiddingBaseManager manager = BiddingManagerFactory.getInstance().createManager(listener, config.getExperiments());
BiddingBaseManager manager = BiddingManagerFactory.getInstance().createManager(null, config.getExperiments());
manager.start();
asyncTokenStorage.getToken(manager);

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

@ -75,9 +75,10 @@ public class AdUnitActivity extends Activity implements IAdUnitActivity {
@Override
protected void onDestroy() {
if (_controller != null) {
_controller.onDestroy();
}
super.onDestroy();
_controller.onDestroy();
}
@Override

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

@ -1,9 +1,15 @@
package com.unity3d.services.ads.adunit;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import android.annotation.TargetApi;
import android.content.Context;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
@ -13,6 +19,7 @@ public class AdUnitRelativeLayout extends RelativeLayout {
private final ArrayList<AdUnitMotionEvent> _motionEvents = new ArrayList<>();
private int _maxEvents = 10000;
private boolean _shouldCapture = false;
private InputEvent _lastInputEvent;
public AdUnitRelativeLayout(Context context) {
super(context);
@ -22,6 +29,13 @@ public class AdUnitRelativeLayout extends RelativeLayout {
public boolean onInterceptTouchEvent(MotionEvent e) {
super.onInterceptTouchEvent(e);
if (e.getActionMasked() == ACTION_UP ||
e.getActionMasked() == ACTION_DOWN ||
e.getActionMasked() == ACTION_POINTER_UP ||
e.getActionMasked() == ACTION_POINTER_DOWN) {
_lastInputEvent = e;
}
if (_shouldCapture) {
if (_motionEvents.size() < _maxEvents) {
boolean isObscured = (e.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0;
@ -102,4 +116,8 @@ public class AdUnitRelativeLayout extends RelativeLayout {
return returnArray;
}
public InputEvent getLastInputEvent() {
return _lastInputEvent;
}
}

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

@ -1,5 +1,6 @@
package com.unity3d.services.ads.api;
import com.unity3d.scar.adapter.common.scarads.UnityAdFormat;
import com.unity3d.services.ads.gmascar.GMAScarAdapterBridge;
import com.unity3d.services.ads.gmascar.GMA;
import com.unity3d.services.core.webview.bridge.WebViewCallback;
@ -30,8 +31,8 @@ public class GMAScar {
}
@WebViewExposed
public static void getSCARSignals(final JSONArray interstitialList, final JSONArray rewardedList, final WebViewCallback callback) throws JSONException {
gmaScarAdapterBridge.getSCARSignals(getPlacementList(interstitialList), getPlacementList(rewardedList));
public static void getSCARSignal(final String placementId, final String adFormatStr, final WebViewCallback callback) {
gmaScarAdapterBridge.getSCARSignal(placementId, UnityAdFormat.valueOf(adFormatStr.toUpperCase()));
callback.invoke();
}
@ -46,13 +47,4 @@ public class GMAScar {
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;
}
}

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

@ -0,0 +1,47 @@
package com.unity3d.services.ads.api;
import android.view.InputEvent;
import com.unity3d.services.ads.measurements.MeasurementsErrors;
import com.unity3d.services.ads.measurements.MeasurementsService;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.webview.bridge.WebViewCallback;
import com.unity3d.services.core.webview.bridge.WebViewExposed;
public class Measurements {
private static final MeasurementsService measurementsService = Utilities.getService(MeasurementsService.class);
@WebViewExposed
public static void checkAvailability(WebViewCallback callback) {
measurementsService.checkAvailability();
callback.invoke();
}
@WebViewExposed
public static void registerView(final String url, WebViewCallback callback) {
measurementsService.registerView(url);
callback.invoke();
}
@WebViewExposed
public static void registerClick(final String url, WebViewCallback callback) {
if (AdUnit.getAdUnitActivity() == null) {
callback.error(MeasurementsErrors.ERROR_AD_UNIT_NULL);
return;
}
if (AdUnit.getAdUnitActivity().getLayout() == null) {
callback.error(MeasurementsErrors.ERROR_LAYOUT_NULL);
return;
}
InputEvent lastInputEvent = AdUnit.getAdUnitActivity().getLayout().getLastInputEvent();
if (lastInputEvent == null) {
callback.error(MeasurementsErrors.ERROR_LAST_INPUT_EVENT_NULL);
return;
}
measurementsService.registerClick(url, lastInputEvent);
callback.invoke();
}
}

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

@ -0,0 +1,21 @@
package com.unity3d.services.ads.api;
import com.unity3d.services.ads.topics.TopicsService;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.webview.bridge.WebViewCallback;
import com.unity3d.services.core.webview.bridge.WebViewExposed;
public class Topics {
private static final TopicsService topicsService = Utilities.getService(TopicsService.class);
@WebViewExposed
public static void checkAvailability(WebViewCallback callback) {
callback.invoke(topicsService.checkAvailability());
}
@WebViewExposed
public static void getTopics(String adsSdkName, Boolean shouldRecordObservation, WebViewCallback callback) {
topicsService.getTopics(adsSdkName, shouldRecordObservation);
callback.invoke();
}
}

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

@ -30,7 +30,9 @@ public class AdsModuleConfiguration implements IAdsModuleConfiguration {
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.GMAScar.class
com.unity3d.services.ads.api.GMAScar.class,
com.unity3d.services.ads.api.Measurements.class,
com.unity3d.services.ads.api.Topics.class,
};
return list;

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

@ -52,7 +52,7 @@ public class GMA {
* @param listener {@link IBiddingSignalsListener} implementation, to be notified when
* signals are ready.
*/
public void getSCARBiddingSignals(IBiddingSignalsListener listener) {
_gmaScarAdapterBridge.getSCARBiddingSignals(new BiddingSignalsHandler(listener));
public void getSCARBiddingSignals(boolean isBannerEnabled, IBiddingSignalsListener listener) {
_gmaScarAdapterBridge.getSCARBiddingSignals(isBannerEnabled, new BiddingSignalsHandler(isBannerEnabled, listener));
}
}

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

@ -1,9 +1,11 @@
package com.unity3d.services.ads.gmascar;
import android.content.Context;
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.scar.adapter.common.scarads.UnityAdFormat;
import com.unity3d.services.ads.gmascar.adapters.ScarAdapterFactory;
import com.unity3d.services.ads.gmascar.bridges.AdapterStatusBridge;
import com.unity3d.services.ads.gmascar.bridges.InitializationStatusBridge;
@ -13,16 +15,17 @@ import com.unity3d.services.ads.gmascar.finder.GMAInitializer;
import com.unity3d.services.ads.gmascar.finder.PresenceDetector;
import com.unity3d.services.ads.gmascar.finder.ScarAdapterVersion;
import com.unity3d.services.ads.gmascar.finder.ScarVersionFinder;
import com.unity3d.services.ads.gmascar.handlers.BiddingSignalsHandler;
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.ads.gmascar.handlers.*;
import com.unity3d.services.ads.gmascar.utils.GMAEventSender;
import com.unity3d.services.banners.BannerView;
import com.unity3d.services.banners.UnityBannerSize;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.core.misc.EventSubject;
import com.unity3d.services.core.properties.ClientProperties;
import com.unity3d.services.core.timer.DefaultIntervalTimerFactory;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
@ -81,12 +84,12 @@ public class GMAScarAdapterBridge {
_scarVersionFinder.getVersion();
}
public void getSCARSignals(String[] interstitialList, String[] rewardedList) {
public void getSCARSignal(String placementId, UnityAdFormat adFormat) {
_scarAdapter = getScarAdapterObject();
SignalsHandler signalListener = new SignalsHandler(_gmaEventSender);
if (_scarAdapter != null) {
_scarAdapter.getSCARSignals(ClientProperties.getApplicationContext(), interstitialList, rewardedList, signalListener);
_scarAdapter.getSCARSignal(ClientProperties.getApplicationContext(), placementId, adFormat, signalListener);
} else {
_webViewErrorHandler.handleError(GMAAdsError.InternalSignalsError("Could not create SCAR adapter object"));
}
@ -111,11 +114,11 @@ public class GMAScarAdapterBridge {
*
* @param handler {@link BiddingSignalsHandler} to be notified when signals are ready.
*/
public void getSCARBiddingSignals(BiddingSignalsHandler handler) {
public void getSCARBiddingSignals(boolean isBannerEnabled, BiddingSignalsHandler handler) {
if (_mobileAdsBridge != null && _mobileAdsBridge.hasSCARBiddingSupport()) {
_scarAdapter = getScarAdapterObject();
if (_scarAdapter != null) {
_scarAdapter.getSCARBiddingSignals(ClientProperties.getApplicationContext(), handler);
_scarAdapter.getSCARBiddingSignals(ClientProperties.getApplicationContext(), isBannerEnabled, handler);
} else {
handler.onSignalsCollectionFailed("Could not create SCAR adapter object.");
}
@ -148,6 +151,16 @@ public class GMAScarAdapterBridge {
_scarAdapter.loadRewardedAd(ClientProperties.getApplicationContext(), scarAdMetadata, adListener);
}
public void loadBanner(final Context context, final BannerView bannerView, final String operationId, final ScarAdMetadata scarAdMetadata, final UnityBannerSize bannerSize) {
_scarAdapter = getScarAdapterObject();
ScarBannerAdHandler adHandler = new ScarBannerAdHandler(operationId);
if (_scarAdapter != null) {
_scarAdapter.loadBannerAd(context, bannerView, scarAdMetadata, bannerSize.getWidth(), bannerSize.getHeight(), adHandler);
} else {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_LOAD_FAILED, operationId);
}
}
public void show(final String placementId, final String queryId, final boolean canSkip) {
ScarAdMetadata scarAdMetadata = new ScarAdMetadata(placementId, queryId);
_scarAdapter = getScarAdapterObject();

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

@ -14,12 +14,6 @@ public class ScarAdapterFactory {
IScarAdapter scarAdapter = null;
switch (adapterVersion) {
case V192:
scarAdapter = new com.unity3d.scar.adapter.v1920.ScarAdapter(adsErrorHandler);
break;
case V195:
scarAdapter = new com.unity3d.scar.adapter.v1950.ScarAdapter(adsErrorHandler);
break;
case V20:
scarAdapter = new com.unity3d.scar.adapter.v2000.ScarAdapter(adsErrorHandler);
break;

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

@ -12,9 +12,6 @@ public class MobileAdsBridgeLegacy extends MobileAdsBridgeBase {
// Codes returned by getVersionString in V20 and below
public static final int CODE_21_0 = 221310000;
public static final int CODE_20_0 = 210402000;
public static final int CODE_19_8 = 204890000;
public static final int CODE_19_5 = 203404000;
public static final int CODE_19_2 = 201604000;
// Deprecated in V21 - requires initialization and returns internal version (e.g., "afma-sdk-a-v<OTA services>.<SCAR version>.X")
public static final String versionStringMethodName = "getVersionString";
@ -44,11 +41,7 @@ public class MobileAdsBridgeLegacy extends MobileAdsBridgeBase {
@Override
public ScarAdapterVersion getAdapterVersion(int versionCode) {
// Version codes in V20 and below are returned as internal nine digit numbers (e.g., "afma-sdk-a-vX.210402000.X)
if (versionCode >= CODE_19_2 && versionCode < CODE_19_5) {
return ScarAdapterVersion.V192;
} else if (versionCode >= CODE_19_5 && versionCode <= CODE_19_8) {
return ScarAdapterVersion.V195;
} else if (versionCode >= CODE_20_0 && versionCode < CODE_21_0) {
if (versionCode >= CODE_20_0 && versionCode < CODE_21_0) {
return ScarAdapterVersion.V20;
}

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

@ -1,8 +1,6 @@
package com.unity3d.services.ads.gmascar.finder;
public enum ScarAdapterVersion {
V192,
V195,
V20,
V21,
NA

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

@ -15,14 +15,16 @@ import org.json.JSONObject;
public class BiddingSignalsHandler implements ISignalCollectionListener {
private final IBiddingSignalsListener listener;
private final boolean isBannerEnabled;
/**
* Constructor that initializes the handler with the passed listener.
*
* @param listener {@link IBiddingSignalsListener} implementation to notify sender.
*/
public BiddingSignalsHandler(IBiddingSignalsListener listener) {
public BiddingSignalsHandler(boolean isBannerEnabled, IBiddingSignalsListener listener) {
this.listener = listener;
this.isBannerEnabled = isBannerEnabled;
}
@Override
@ -39,6 +41,14 @@ public class BiddingSignalsHandler implements ISignalCollectionListener {
private BiddingSignals getSignals(String signalsMap) {
try {
JSONObject signalsJson = new JSONObject(signalsMap);
if (isBannerEnabled) {
return new BiddingSignals(
getSignalFromJson(signalsJson, SignalsCollectorBase.SCAR_RV_SIGNAL),
getSignalFromJson(signalsJson, SignalsCollectorBase.SCAR_INT_SIGNAL),
getSignalFromJson(signalsJson, SignalsCollectorBase.SCAR_BAN_SIGNAL)
);
}
return new BiddingSignals(
getSignalFromJson(signalsJson, SignalsCollectorBase.SCAR_RV_SIGNAL),
getSignalFromJson(signalsJson, SignalsCollectorBase.SCAR_INT_SIGNAL)

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

@ -46,11 +46,6 @@ public abstract class ScarAdHandlerBase implements IScarAdListenerWrapper {
_gmaEventSender.send(GMAEvent.AD_CLICKED);
}
@Override
public void onAdSkipped() {
_gmaEventSender.send(GMAEvent.AD_SKIPPED);
}
@Override
public void onAdClosed() {
_gmaEventSender.send(GMAEvent.AD_CLOSED);

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

@ -0,0 +1,48 @@
package com.unity3d.services.ads.gmascar.handlers;
import com.unity3d.scar.adapter.common.IScarBannerAdListenerWrapper;
import com.unity3d.services.banners.BannerViewCache;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.core.webview.WebViewApp;
import com.unity3d.services.core.webview.WebViewEventCategory;
public class ScarBannerAdHandler implements IScarBannerAdListenerWrapper {
private String _operationId;
public ScarBannerAdHandler(String operationId) {
_operationId = operationId;
}
@Override
public void onAdLoaded() {
BannerViewCache.getInstance().addScarContainer(_operationId);
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_LOADED, _operationId);
}
@Override
public void onAdFailedToLoad(int errorCode, String errorString) {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_LOAD_FAILED, _operationId, errorCode, errorString);
}
@Override
public void onAdOpened() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_OPENED, _operationId);
}
@Override
public void onAdClicked() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_CLICKED, _operationId);
}
@Override
public void onAdClosed() {
// Code to be executed when the user is about to return to the app after tapping on an ad.
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_CLOSED, _operationId);
}
@Override
public void onAdImpression() {
WebViewApp.getCurrentApp().sendEvent(WebViewEventCategory.BANNER, BannerBridge.BannerEvent.SCAR_BANNER_IMPRESSION, _operationId);
}
}

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

@ -17,10 +17,15 @@ public class ScarInterstitialAdHandler extends ScarAdHandlerBase implements ISca
_gmaEventSender.send(GMAEvent.INTERSTITIAL_SHOW_ERROR, _scarAdMetadata.getPlacementId(), _scarAdMetadata.getQueryId(), errorString, errorCode);
}
@Override
public void onAdSkipped() {
_gmaEventSender.send(GMAEvent.AD_SKIPPED);
}
@Override
public void onAdClosed() {
if (!_eventSubject.eventQueueIsEmpty()) {
super.onAdSkipped();
onAdSkipped();
}
super.onAdClosed();
}

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

@ -25,10 +25,15 @@ public class ScarRewardedAdHandler extends ScarAdHandlerBase implements IScarRew
_gmaEventSender.send(GMAEvent.AD_EARNED_REWARD);
}
@Override
public void onAdSkipped() {
_gmaEventSender.send(GMAEvent.AD_SKIPPED);
}
@Override
public void onAdClosed() {
if (!_hasEarnedReward) {
super.onAdSkipped();
onAdSkipped();
}
super.onAdClosed();
}

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

@ -11,7 +11,6 @@ import com.unity3d.services.ads.gmascar.utils.ScarRequestHandler;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.request.metrics.SDKMetrics;
import com.unity3d.services.core.request.metrics.ScarMetric;
import java.util.UUID;
@ -25,16 +24,17 @@ public abstract class BiddingBaseManager implements IBiddingManager {
private final IUnityAdsTokenListener unityAdsTokenListener;
private final ScarRequestHandler _scarRequestHandler;
private final boolean _isAsyncTokenCall;
private final boolean _isBannerEnabled;
private final AtomicReference<BiddingSignals> signals = new AtomicReference<>();
public BiddingBaseManager(IUnityAdsTokenListener unityAdsTokenListener) {
this(unityAdsTokenListener, new ScarRequestHandler());
public BiddingBaseManager(boolean isBannerEnabled, IUnityAdsTokenListener unityAdsTokenListener) {
this(isBannerEnabled, unityAdsTokenListener, new ScarRequestHandler());
}
public BiddingBaseManager(IUnityAdsTokenListener unityAdsTokenListener, ScarRequestHandler requestSender) {
public BiddingBaseManager(boolean isBannerEnabled, IUnityAdsTokenListener unityAdsTokenListener, ScarRequestHandler requestSender) {
this.tokenIdentifier = UUID.randomUUID().toString();
this._isBannerEnabled = isBannerEnabled;
this.unityAdsTokenListener = unityAdsTokenListener;
this._isAsyncTokenCall = unityAdsTokenListener != null;
this._scarRequestHandler = requestSender;
@ -42,8 +42,6 @@ public abstract class BiddingBaseManager implements IBiddingManager {
public abstract void start();
public abstract void onUnityTokenSuccessfullyFetched();
@Override
public String getTokenIdentifier() {
return tokenIdentifier;
@ -53,7 +51,7 @@ public abstract class BiddingBaseManager implements IBiddingManager {
public String getFormattedToken(String unityToken) {
if (unityToken == null || unityToken.isEmpty()) return null;
String tokenIdentifier = getTokenIdentifier();
if (tokenIdentifier == null || (tokenIdentifier != null && tokenIdentifier.isEmpty())) return unityToken;
if (tokenIdentifier == null || tokenIdentifier.isEmpty()) return unityToken;
else return String.format(TOKEN_WITH_SCAR_FORMAT, tokenIdentifier, unityToken);
}
@ -62,10 +60,6 @@ public abstract class BiddingBaseManager implements IBiddingManager {
if (unityAdsTokenListener != null) {
wrapCustomerListener(() -> unityAdsTokenListener.onUnityAdsTokenReady(token));
}
if (token != null && !token.isEmpty()) {
onUnityTokenSuccessfullyFetched();
}
}
public void permitUpload() {
@ -80,23 +74,18 @@ public abstract class BiddingBaseManager implements IBiddingManager {
public void fetchSignals() {
getMetricSender().sendMetric(ScarMetric.hbSignalsFetchStart(_isAsyncTokenCall));
new Thread(new Runnable() {
new Thread(() -> GMA.getInstance().getSCARBiddingSignals(this._isBannerEnabled, new IBiddingSignalsListener() {
@Override
public void run() {
GMA.getInstance().getSCARBiddingSignals(new IBiddingSignalsListener() {
@Override
public void onSignalsReady(BiddingSignals signals) {
BiddingBaseManager.this.onSignalsReady(signals);
sendFetchResult("");
}
@Override
public void onSignalsFailure(String msg) {
sendFetchResult(msg);
}
});
public void onSignalsReady(BiddingSignals signals) {
BiddingBaseManager.this.onSignalsReady(signals);
sendFetchResult("");
}
}).start();
@Override
public void onSignalsFailure(String msg) {
sendFetchResult(msg);
}
})).start();
}
public void sendFetchResult(String errorMsg) {
@ -129,17 +118,14 @@ public abstract class BiddingBaseManager implements IBiddingManager {
return;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
// Since there are potential side effects of file read in the
// getCurrentConfiguration call, we don't want this to be called in the constructor.
_scarRequestHandler.makeUploadRequest(tokenIdentifier, signals, new ConfigurationReader().getCurrentConfiguration().getScarBiddingUrl());
getMetricSender().sendMetric(ScarMetric.hbSignalsUploadSuccess(_isAsyncTokenCall));
} catch (Exception e) {
getMetricSender().sendMetric(ScarMetric.hbSignalsUploadFailure(_isAsyncTokenCall, e.getLocalizedMessage()));
}
new Thread(() -> {
try {
// Since there are potential side effects of file read in the
// getCurrentConfiguration call, we don't want this to be called in the constructor.
_scarRequestHandler.makeUploadRequest(tokenIdentifier, signals, new ConfigurationReader().getCurrentConfiguration().getScarBiddingUrl());
getMetricSender().sendMetric(ScarMetric.hbSignalsUploadSuccess(_isAsyncTokenCall));
} catch (Exception e) {
getMetricSender().sendMetric(ScarMetric.hbSignalsUploadFailure(_isAsyncTokenCall, e.getLocalizedMessage()));
}
}).start();
}

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

@ -5,7 +5,7 @@ import com.unity3d.ads.IUnityAdsTokenListener;
public class BiddingDisabledManager extends BiddingBaseManager {
public BiddingDisabledManager(IUnityAdsTokenListener unityAdsTokenListener) {
super(unityAdsTokenListener);
super(false, unityAdsTokenListener);
}
@Override
@ -18,9 +18,4 @@ public class BiddingDisabledManager extends BiddingBaseManager {
public void start() {
// SCAR bidding signals collection should be blocked.
}
@Override
public void onUnityTokenSuccessfullyFetched() {
// SCAR bidding signals collection should be blocked.
}
}
}

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

@ -4,8 +4,8 @@ import com.unity3d.ads.IUnityAdsTokenListener;
public class BiddingEagerManager extends BiddingBaseManager {
public BiddingEagerManager(IUnityAdsTokenListener unityAdsTokenListener) {
super(unityAdsTokenListener);
public BiddingEagerManager(boolean isBannerEnabled, IUnityAdsTokenListener unityAdsTokenListener) {
super(isBannerEnabled, unityAdsTokenListener);
}
@Override
@ -13,12 +13,4 @@ public class BiddingEagerManager extends BiddingBaseManager {
permitSignalsUpload();
fetchSignals();
}
@Override
public void onUnityTokenSuccessfullyFetched() {
// Not relevant.
//
// Signals upload will be start once scar signals are ready regardless of unityToken
// validity.
}
}

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

@ -1,23 +0,0 @@
package com.unity3d.services.ads.gmascar.managers;
import com.unity3d.ads.IUnityAdsTokenListener;
public class BiddingLazyManager extends BiddingBaseManager {
public BiddingLazyManager(IUnityAdsTokenListener unityAdsTokenListener) {
super(unityAdsTokenListener);
}
@Override
public void start() {
// Not relevant.
//
// Signals fetch will be start once valid unity token is fetched.
}
@Override
public void onUnityTokenSuccessfullyFetched() {
permitSignalsUpload();
fetchSignals();
}
}

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

@ -21,40 +21,10 @@ public class BiddingManagerFactory {
public BiddingBaseManager createManager(IUnityAdsTokenListener unityAdsTokenListener,
IExperiments experiments) {
if (GMA.getInstance().hasSCARBiddingSupport()) {
return getExperiment(unityAdsTokenListener, experiments);
boolean isBannerEnabled = experiments != null && experiments.isScarBannerHbEnabled();
return new BiddingEagerManager(isBannerEnabled, unityAdsTokenListener);
}
return new BiddingDisabledManager(unityAdsTokenListener);
}
private BiddingBaseManager getExperiment(IUnityAdsTokenListener unityAdsTokenListener,
IExperiments experiments) {
if (experiments == null || experiments.getScarBiddingManager() == null) {
return new BiddingDisabledManager(unityAdsTokenListener);
}
String biddingManager = experiments.getScarBiddingManager();
SCARBiddingManagerType biddingManagerType = SCARBiddingManagerType.fromName(biddingManager);
/*
If unityAdsTokenListener is null it is a synchronous getToken call and we should use
the EAGER bidding manager if part of any of the enabled experiment types since we do not
have to listen for a token fetch result
*/
if (unityAdsTokenListener == null && biddingManagerType != SCARBiddingManagerType.DISABLED) {
return new BiddingEagerManager(null);
}
switch (biddingManagerType) {
case EAGER:
return new BiddingEagerManager(unityAdsTokenListener);
case LAZY:
return new BiddingLazyManager(unityAdsTokenListener);
case HYBRID:
return new BiddingOnDemandManager(unityAdsTokenListener);
case DISABLED:
default:
return new BiddingDisabledManager(unityAdsTokenListener);
}
}
}
}

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

@ -1,20 +0,0 @@
package com.unity3d.services.ads.gmascar.managers;
import com.unity3d.ads.IUnityAdsTokenListener;
public class BiddingOnDemandManager extends BiddingBaseManager {
public BiddingOnDemandManager(IUnityAdsTokenListener unityAdsTokenListener) {
super(unityAdsTokenListener);
}
@Override
public void start() {
fetchSignals();
}
@Override
public void onUnityTokenSuccessfullyFetched() {
permitSignalsUpload();
}
}

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

@ -1,39 +0,0 @@
package com.unity3d.services.ads.gmascar.managers;
public enum SCARBiddingManagerType {
DISABLED(Constants.DIS),
EAGER(Constants.EAG),
LAZY(Constants.LAZ),
HYBRID(Constants.HYB);
private final String name;
SCARBiddingManagerType(final String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public static SCARBiddingManagerType fromName(final String name) {
switch(name) {
case Constants.EAG:
return EAGER;
case Constants.LAZ:
return LAZY;
case Constants.HYB:
return HYBRID;
case Constants.DIS:
default:
return DISABLED;
}
}
private static class Constants {
private static final String LAZ = "laz";
private static final String EAG = "eag";
private static final String HYB = "hyb";
private static final String DIS = "dis";
}
}

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

@ -7,8 +7,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import static com.unity3d.services.ads.gmascar.utils.ScarConstants.IN_SIGNAL_KEY;
import static com.unity3d.services.ads.gmascar.utils.ScarConstants.RV_SIGNAL_KEY;
import static com.unity3d.services.ads.gmascar.utils.ScarConstants.*;
/**
* Data structure for GMA bidding signals.
@ -17,6 +16,7 @@ public class BiddingSignals {
private final String rvSignal;
private final String interstitialSignal;
private final String bannerSignal;
/**
* Constructor that initialized the object with passed GMA bidding signals.
@ -24,9 +24,16 @@ public class BiddingSignals {
* @param rvSignal rewarded video GMA Scar bidding signal.
* @param interstitialSignal interstitial GMA Scar bidding signal.
*/
public BiddingSignals(String rvSignal, String interstitialSignal, String bannerSignal) {
this.rvSignal = rvSignal;
this.interstitialSignal = interstitialSignal;
this.bannerSignal = bannerSignal;
}
public BiddingSignals(String rvSignal, String interstitialSignal) {
this.rvSignal = rvSignal;
this.interstitialSignal = interstitialSignal;
this.bannerSignal = "";
}
/**
@ -50,12 +57,22 @@ public class BiddingSignals {
}
/**
* Checks if both bidding signals are empty in which case they should not be uploaded
* Getter for banner GMA SCAR signal.
*
* @return true if both signals are empty, false otherwise
* @return banner bidding signal
*/
@Nullable
public String getBannerSignal() {
return bannerSignal;
}
/**
* Checks if all bidding signals are empty in which case they should not be uploaded
*
* @return true if all signals are empty, false otherwise
*/
public boolean isEmpty () {
return TextUtils.isEmpty(getRvSignal()) && TextUtils.isEmpty(getInterstitialSignal());
return TextUtils.isEmpty(getRvSignal()) && TextUtils.isEmpty(getInterstitialSignal()) && TextUtils.isEmpty(getBannerSignal());
}
/**
@ -74,6 +91,10 @@ public class BiddingSignals {
signalsMap.put(IN_SIGNAL_KEY, getInterstitialSignal());
}
if (!TextUtils.isEmpty(getBannerSignal())) {
signalsMap.put(BN_SIGNAL_KEY, getBannerSignal());
}
return signalsMap;
}
}

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

@ -6,6 +6,8 @@ public class ScarConstants {
public static final String TOKEN_ID_KEY = "tid";
public static final String RV_SIGNAL_KEY = "rv";
public static final String IN_SIGNAL_KEY = "in";
public static final String BN_SIGNAL_KEY = "bn";
public static final String SCAR_TOKEN_IDENTIFIER_KEY = "scarId";
public static final String TOKEN_WITH_SCAR_FORMAT = "%s:%s";
}

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

@ -0,0 +1,12 @@
package com.unity3d.services.ads.measurements
enum class MeasurementsErrors {
ERROR_AD_SERVICES_DISABLED,
ERROR_EXTENSION_BELOW_4,
ERROR_API_BELOW_33,
ERROR_MANAGER_NULL,
ERROR_EXCEPTION,
ERROR_AD_UNIT_NULL,
ERROR_LAYOUT_NULL,
ERROR_LAST_INPUT_EVENT_NULL,
}

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

@ -0,0 +1,10 @@
package com.unity3d.services.ads.measurements
enum class MeasurementsEvents {
NOT_AVAILABLE,
AVAILABLE,
VIEW_SUCCESSFUL,
VIEW_ERROR,
CLICK_SUCCESSFUL,
CLICK_ERROR,
}

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

@ -0,0 +1,21 @@
package com.unity3d.services.ads.measurements
import android.annotation.SuppressLint
import android.os.OutcomeReceiver
import com.unity3d.services.core.webview.WebViewEventCategory
import com.unity3d.services.core.webview.bridge.IEventSender
@SuppressLint("NewApi", "MissingPermission")
class MeasurementsReceiver(
private val eventSender: IEventSender,
private val successEvent: MeasurementsEvents,
private val errorEvent: MeasurementsEvents,
): OutcomeReceiver<Any, Exception> {
override fun onResult(p0: Any) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, successEvent)
}
override fun onError(error: Exception) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, errorEvent, error.toString())
}
}

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

@ -0,0 +1,76 @@
package com.unity3d.services.ads.measurements
import android.adservices.AdServicesState
import android.adservices.measurement.MeasurementManager
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import android.view.InputEvent
import com.unity3d.services.core.device.Device
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.webview.WebViewEventCategory
import com.unity3d.services.core.webview.bridge.IEventSender
import kotlinx.coroutines.asExecutor
@SuppressLint("NewApi", "MissingPermission")
class MeasurementsService(context: Context, private val dispatchers: ISDKDispatchers, private val eventSender: IEventSender) {
private val measurementManager: MeasurementManager? = getMeasurementManager(context)
fun checkAvailability() {
if (Device.getApiLevel() < 33) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, MeasurementsEvents.NOT_AVAILABLE, MeasurementsErrors.ERROR_API_BELOW_33)
return
}
if (SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) < 4) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, MeasurementsEvents.NOT_AVAILABLE, MeasurementsErrors.ERROR_EXTENSION_BELOW_4)
return
}
if (measurementManager == null) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, MeasurementsEvents.NOT_AVAILABLE, MeasurementsErrors.ERROR_MANAGER_NULL)
return
}
if (!AdServicesState.isAdServicesStateEnabled()) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, MeasurementsEvents.NOT_AVAILABLE, MeasurementsErrors.ERROR_AD_SERVICES_DISABLED)
return
}
measurementManager.getMeasurementApiStatus(dispatchers.default.asExecutor(), MeasurementsStatusReceiver(eventSender))
}
fun registerView(url: String) {
measurementManager?.registerSource(
Uri.parse(url),
null,
dispatchers.default.asExecutor(),
MeasurementsReceiver(eventSender, MeasurementsEvents.VIEW_SUCCESSFUL, MeasurementsEvents.VIEW_ERROR)
)
}
fun registerClick(url: String, inputEvent: InputEvent) {
measurementManager?.registerSource(
Uri.parse(url),
inputEvent,
dispatchers.default.asExecutor(),
MeasurementsReceiver(eventSender, MeasurementsEvents.CLICK_SUCCESSFUL, MeasurementsEvents.CLICK_ERROR)
)
}
private fun getMeasurementManager(context: Context): MeasurementManager? {
// accessing MeasurementManager without API level and extension version checks can crash old Android devices
// also accessing SdkExtensions below API level 30 causes exception so check API level first
if (Device.getApiLevel() < 33) {
return null
}
if (SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) < 4) {
return null
}
return context.getSystemService(MeasurementManager::class.java)
}
}

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

@ -0,0 +1,17 @@
package com.unity3d.services.ads.measurements
import android.annotation.SuppressLint
import android.os.OutcomeReceiver
import com.unity3d.services.core.webview.WebViewEventCategory
import com.unity3d.services.core.webview.bridge.IEventSender
@SuppressLint("NewApi", "MissingPermission")
class MeasurementsStatusReceiver(private val eventSender: IEventSender) : OutcomeReceiver<Int, Exception> {
override fun onResult(status: Int) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, MeasurementsEvents.AVAILABLE, status)
}
override fun onError(error: Exception) {
eventSender.sendEvent(WebViewEventCategory.MEASUREMENTS, MeasurementsEvents.NOT_AVAILABLE, MeasurementsErrors.ERROR_EXCEPTION, error.toString())
}
}

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

@ -1,13 +1,9 @@
package com.unity3d.services.ads.operation.load;
import com.unity3d.services.banners.BannerViewCache;
import com.unity3d.services.banners.UnityBannerSize;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.configuration.InitializationNotificationCenter;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.request.metrics.SDKMetrics;
import org.json.JSONException;
import org.json.JSONObject;
@ -20,7 +16,7 @@ public class LoadBannerModule extends BaseLoadModule {
if (_instance == null) {
LoadBannerModule loadModule = new LoadBannerModule(Utilities.getService(SDKMetricsSender.class));
LoadModuleDecoratorInitializationBuffer bufferedLoadModule = new LoadModuleDecoratorInitializationBuffer(loadModule, InitializationNotificationCenter.getInstance());
LoadModuleDecoratorTimeout timedLoadModule = new LoadModuleDecoratorTimeout(bufferedLoadModule, new ConfigurationReader());
LoadModuleDecoratorTimeout timedLoadModule = new LoadModuleDecoratorTimeout(bufferedLoadModule, new ExperimentsReader());
_instance = timedLoadModule;
}
return _instance;
@ -37,20 +33,4 @@ public class LoadBannerModule extends BaseLoadModule {
parameters.put("height", ((LoadBannerOperationState) state).getSize().getHeight());
}
}
@Override
public void onUnityAdsAdLoaded(String operationId) {
final ILoadOperation loadOperation = get(operationId);
if (loadOperation == null || loadOperation.getLoadOperationState() == null) return;
final LoadOperationState state = loadOperation.getLoadOperationState();
if (state instanceof LoadBannerOperationState) {
String bannerAdId = state.getId();
UnityBannerSize size = ((LoadBannerOperationState) state).getSize();
boolean successfullyLoaded = BannerViewCache.getInstance().loadWebPlayer(bannerAdId, size);
if (successfullyLoaded) {
BannerBridge.didLoad(operationId);
}
super.onUnityAdsAdLoaded(operationId);
}
}
}

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

@ -2,11 +2,13 @@ package com.unity3d.services.ads.operation.load;
import com.unity3d.ads.IUnityAdsLoadListener;
import com.unity3d.ads.UnityAdsLoadOptions;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.services.banners.UnityBannerSize;
import com.unity3d.services.core.configuration.Configuration;
public class LoadBannerOperationState extends LoadOperationState {
private UnityBannerSize _size;
private ScarAdMetadata _scarAdMetadata;
public LoadBannerOperationState(String placementId, String bannerAdId, UnityBannerSize size, IUnityAdsLoadListener listener, UnityAdsLoadOptions loadOptions, Configuration configuration) {
super(placementId, listener, loadOptions, configuration);
@ -21,4 +23,16 @@ public class LoadBannerOperationState extends LoadOperationState {
public void setSize(UnityBannerSize size) {
_size = size;
}
public void setScarAdMetadata(ScarAdMetadata scarAdMetadata) {
_scarAdMetadata = scarAdMetadata;
}
public ScarAdMetadata getScarAdMetadata() {
return _scarAdMetadata;
}
public boolean isScarAd() {
return _scarAdMetadata != null;
}
}

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

@ -1,13 +1,11 @@
package com.unity3d.services.ads.operation.load;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.configuration.InitializationNotificationCenter;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.request.metrics.SDKMetrics;
import org.json.JSONException;
import org.json.JSONObject;
public class LoadModule extends BaseLoadModule {
@ -18,7 +16,7 @@ public class LoadModule extends BaseLoadModule {
if (_instance == null) {
LoadModule loadModule = new LoadModule(Utilities.getService(SDKMetricsSender.class));
LoadModuleDecoratorInitializationBuffer bufferedLoadModule = new LoadModuleDecoratorInitializationBuffer(loadModule, InitializationNotificationCenter.getInstance());
LoadModuleDecoratorTimeout timedLoadModule = new LoadModuleDecoratorTimeout(bufferedLoadModule, new ConfigurationReader());
LoadModuleDecoratorTimeout timedLoadModule = new LoadModuleDecoratorTimeout(bufferedLoadModule, new ExperimentsReader());
_instance = timedLoadModule;
}
return _instance;

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

@ -1,7 +1,7 @@
package com.unity3d.services.ads.operation.load;
import com.unity3d.ads.UnityAds;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.request.metrics.AdOperationError;
import com.unity3d.services.core.request.metrics.AdOperationMetric;
import com.unity3d.services.core.timer.BaseTimer;
@ -13,15 +13,20 @@ import java.util.concurrent.Executors;
public class LoadModuleDecoratorTimeout extends LoadModuleDecorator {
private static final String errorMsgTimeoutLoading = "[UnityAds] Timeout while loading ";
public LoadModuleDecoratorTimeout(ILoadModule loadModule, ConfigurationReader configurationReader) {
private final ExperimentsReader _experimentsReader;
public LoadModuleDecoratorTimeout(ILoadModule loadModule, ExperimentsReader experimentsReader) {
super(loadModule);
this._experimentsReader = experimentsReader;
}
@Override
public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, LoadOperationState state) {
getMetricSender().sendMetricWithInitState(AdOperationMetric.newAdLoadStart());
state.start();
startLoadTimeout(state);
if (!_experimentsReader.getCurrentlyActiveExperiments().isNativeLoadTimeoutDisabled()) {
startLoadTimeout(state);
}
super.executeAdOperation(webViewBridgeInvoker, state);
}

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

@ -10,14 +10,14 @@ import android.view.WindowManager;
import com.unity3d.ads.UnityAds;
import com.unity3d.services.ads.operation.AdModule;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.device.Device;
import com.unity3d.services.core.device.reader.HdrInfoReader;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.properties.ClientProperties;
import com.unity3d.services.core.request.metrics.AdOperationError;
import com.unity3d.services.core.request.metrics.AdOperationMetric;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import com.unity3d.services.core.request.metrics.SDKMetrics;
import com.unity3d.services.core.webview.bridge.CallbackStatus;
import com.unity3d.services.core.webview.bridge.IWebViewBridgeInvoker;
import com.unity3d.services.core.webview.bridge.invocation.IWebViewBridgeInvocationCallback;
@ -34,7 +34,7 @@ public class ShowModule extends AdModule<IShowOperation, ShowOperationState> imp
public static IShowModule getInstance() {
if (instance == null) {
instance = new ShowModuleDecoratorTimeout(new ShowModule(Utilities.getService(SDKMetricsSender.class)), new ConfigurationReader());
instance = new ShowModuleDecoratorTimeout(new ShowModule(Utilities.getService(SDKMetricsSender.class)), new ExperimentsReader());
}
return instance;
}
@ -106,6 +106,7 @@ public class ShowModule extends AdModule<IShowOperation, ShowOperationState> imp
set(showOperation);
showOperation.invoke(state.configuration.getWebViewBridgeTimeout(), parameters);
HdrInfoReader.getInstance().captureHDRCapabilityMetrics(activity, new ExperimentsReader());
}
public void onUnityAdsShowFailure(String id, UnityAds.UnityAdsShowError error, String message) {

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

@ -1,7 +1,7 @@
package com.unity3d.services.ads.operation.show;
import com.unity3d.ads.UnityAds;
import com.unity3d.services.core.configuration.ConfigurationReader;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.request.metrics.AdOperationError;
import com.unity3d.services.core.request.metrics.AdOperationMetric;
import com.unity3d.services.core.timer.BaseTimer;
@ -13,15 +13,20 @@ import java.util.concurrent.Executors;
public class ShowModuleDecoratorTimeout extends ShowModuleDecorator {
private static final String errorMsgTimeout = "[UnityAds] Timeout while trying to show ";
public ShowModuleDecoratorTimeout(IShowModule showModule, ConfigurationReader configurationReader) {
private final ExperimentsReader _experimentsReader;
public ShowModuleDecoratorTimeout(IShowModule showModule, ExperimentsReader experimentsReader) {
super(showModule);
this._experimentsReader = experimentsReader;
}
@Override
public void executeAdOperation(IWebViewBridgeInvoker webViewBridgeInvoker, ShowOperationState state) {
getMetricSender().sendMetricWithInitState(AdOperationMetric.newAdShowStart());
state.start();
startShowTimeout(state);
if (!_experimentsReader.getCurrentlyActiveExperiments().isNativeShowTimeoutDisabled()) {
startShowTimeout(state);
}
super.executeAdOperation(webViewBridgeInvoker, state);
}

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

@ -0,0 +1,5 @@
package com.unity3d.services.ads.topics
enum class TopicsErrors {
ERROR_EXCEPTION,
}

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

@ -0,0 +1,6 @@
package com.unity3d.services.ads.topics
enum class TopicsEvents {
NOT_AVAILABLE,
TOPICS_AVAILABLE,
}

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

@ -0,0 +1,35 @@
package com.unity3d.services.ads.topics
import android.adservices.topics.GetTopicsResponse
import android.adservices.topics.Topic
import android.annotation.SuppressLint
import android.os.OutcomeReceiver
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.webview.WebViewEventCategory
import com.unity3d.services.core.webview.bridge.IEventSender
import org.json.JSONArray
import org.json.JSONObject
@SuppressLint("NewApi", "MissingPermission")
class TopicsReceiver(private val eventSender: IEventSender) : OutcomeReceiver<GetTopicsResponse, Exception> {
override fun onResult(result: GetTopicsResponse) {
val resultArray = JSONArray()
result.topics.forEach {
resultArray.put(formatTopic(it))
}
eventSender.sendEvent(WebViewEventCategory.TOPICS, TopicsEvents.TOPICS_AVAILABLE, resultArray.toString())
}
override fun onError(error: Exception) {
DeviceLog.debug("GetTopics exception: $error")
eventSender.sendEvent(WebViewEventCategory.TOPICS, TopicsEvents.NOT_AVAILABLE, TopicsErrors.ERROR_EXCEPTION, error.toString())
}
fun formatTopic(topic: Topic): JSONObject {
val resultObject = JSONObject()
resultObject.put("taxonomyVersion", topic.taxonomyVersion)
resultObject.put("modelVersion", topic.modelVersion)
resultObject.put("topicId", topic.topicId)
return resultObject
}
}

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

@ -0,0 +1,64 @@
package com.unity3d.services.ads.topics
import android.adservices.AdServicesState
import android.adservices.topics.GetTopicsRequest
import android.adservices.topics.TopicsManager
import android.annotation.SuppressLint
import android.content.Context
import android.os.ext.SdkExtensions
import com.unity3d.services.core.device.Device
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.webview.WebViewEventCategory
import com.unity3d.services.core.webview.bridge.IEventSender
import kotlinx.coroutines.asExecutor
@SuppressLint("NewApi", "MissingPermission")
class TopicsService(context: Context, private val dispatchers: ISDKDispatchers, private val eventSender: IEventSender) {
private val topicsManager: TopicsManager? = getTopicsManager(context)
fun checkAvailability(): TopicsStatus {
if (Device.getApiLevel() < 33) {
return TopicsStatus.ERROR_API_BELOW_33
}
if (SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) < 4) {
return TopicsStatus.ERROR_EXTENSION_BELOW_4
}
if (topicsManager == null) {
return TopicsStatus.ERROR_TOPICSMANAGER_NULL
}
if (!AdServicesState.isAdServicesStateEnabled()) {
return TopicsStatus.ERROR_AD_SERVICES_DISABLED
}
return TopicsStatus.TOPICS_AVAILABLE
}
fun getTopics(adsSdkName: String, shouldRecordObservation: Boolean) {
val callback = TopicsReceiver(eventSender)
val topicsRequest = GetTopicsRequest.Builder().setAdsSdkName(adsSdkName).setShouldRecordObservation(shouldRecordObservation).build()
try {
topicsManager?.getTopics(topicsRequest, dispatchers.default.asExecutor(), callback)
} catch (error: Exception) {
eventSender.sendEvent(WebViewEventCategory.TOPICS, TopicsEvents.NOT_AVAILABLE, TopicsErrors.ERROR_EXCEPTION, error.toString())
DeviceLog.debug("Failed to get topics with error: $error")
}
}
private fun getTopicsManager(context: Context): TopicsManager? {
// accessing TopicsManager without API level and extension version checks can crash old Android devices
// also accessing SdkExtensions below API level 30 causes exception so check API level first
if (Device.getApiLevel() < 33) {
return null
}
if (SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) < 4) {
return null
}
return context.getSystemService(TopicsManager::class.java)
}
}

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

@ -0,0 +1,9 @@
package com.unity3d.services.ads.topics
enum class TopicsStatus {
TOPICS_AVAILABLE,
ERROR_TOPICSMANAGER_NULL,
ERROR_API_BELOW_33,
ERROR_EXTENSION_BELOW_4,
ERROR_AD_SERVICES_DISABLED,
}

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

@ -11,6 +11,18 @@ public class BannerErrorInfo {
this.errorMessage = errorMessage;
}
public UnityAds.UnityAdsLoadError toLoadError() {
switch (errorCode) {
case NATIVE_ERROR:
return UnityAds.UnityAdsLoadError.INVALID_ARGUMENT;
case NO_FILL:
return UnityAds.UnityAdsLoadError.NO_FILL;
case WEBVIEW_ERROR:
return UnityAds.UnityAdsLoadError.INTERNAL_ERROR;
}
return UnityAds.UnityAdsLoadError.INTERNAL_ERROR;
}
public static BannerErrorInfo fromLoadError(UnityAds.UnityAdsLoadError error, String message) {
switch (error) {
case INITIALIZE_FAILED:
@ -19,6 +31,8 @@ public class BannerErrorInfo {
return new BannerErrorInfo(message, BannerErrorCode.NATIVE_ERROR);
case INTERNAL_ERROR:
return new BannerErrorInfo(message, BannerErrorCode.WEBVIEW_ERROR);
case NO_FILL:
return new BannerErrorInfo(message, BannerErrorCode.NO_FILL);
default:
return new BannerErrorInfo(message, BannerErrorCode.UNKNOWN);
}

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

@ -7,9 +7,13 @@ import android.view.ViewParent;
import android.widget.RelativeLayout;
import com.unity3d.ads.UnityAdsLoadOptions;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.services.ads.gmascar.GMA;
import com.unity3d.services.ads.gmascar.GMAScarAdapterBridge;
import com.unity3d.services.ads.webplayer.WebPlayerSettingsCache;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.banners.view.BannerWebPlayerContainer;
import com.unity3d.services.banners.view.ScarBannerContainer;
import com.unity3d.services.core.configuration.ErrorState;
import com.unity3d.services.core.configuration.IInitializationListener;
import com.unity3d.services.core.configuration.InitializationNotificationCenter;
@ -29,7 +33,9 @@ public class BannerView extends RelativeLayout {
private UnityBannerSize size;
private IListener listener;
private BannerWebPlayerContainer bannerWebPlayerContainer;
private ScarBannerContainer scarBannerContainer;
private IInitializationListener initializationListener;
private final GMAScarAdapterBridge gmaScarAdapterBridge = GMA.getInstance().getBridge();
// Public
@ -95,6 +101,10 @@ public class BannerView extends RelativeLayout {
bannerWebPlayerContainer.destroy();
}
if (scarBannerContainer != null) {
scarBannerContainer.destroy();
}
// Log the banner was destroyed
DeviceLog.info("Banner [" + this.placementId + "] was destroyed");
@ -139,6 +149,15 @@ public class BannerView extends RelativeLayout {
// Module Private
void loadScarPlayer(String operationId, ScarAdMetadata scarAdMetadata, UnityBannerSize size) {
gmaScarAdapterBridge.loadBanner(getContext(), this, operationId, scarAdMetadata, size);
}
public void addScarContainer() {
scarBannerContainer = new ScarBannerContainer(getContext(), viewId);
Utilities.runOnUiThread(() -> addView(scarBannerContainer));
}
void loadWebPlayer(final UnityBannerSize unityBannerSize) {
final BannerView self = this;
Utilities.runOnUiThread(new Runnable() {

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

@ -1,8 +1,9 @@
package com.unity3d.services.banners;
import com.unity3d.ads.UnityAds;
import com.unity3d.services.ads.operation.load.ILoadModule;
import com.unity3d.services.ads.operation.load.LoadBannerModule;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.services.ads.operation.load.*;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.core.misc.Utilities;
import java.lang.ref.WeakReference;
@ -44,6 +45,37 @@ public class BannerViewCache {
_bannerViews.remove(bannerAdId);
}
public synchronized void loadBanner(LoadBannerOperationState state) {
String bannerAdId = state.getId();
UnityBannerSize size = state.getSize();
if (state.isScarAd()) {
ScarAdMetadata scarAdMetadata = state.getScarAdMetadata();
loadScarPlayer(bannerAdId, scarAdMetadata, size);
} else {
boolean successfullyLoaded = loadWebPlayer(bannerAdId, size);
if (successfullyLoaded) {
BannerBridge.didLoad(bannerAdId);
}
}
}
public synchronized void loadScarPlayer(String bannerAdId, ScarAdMetadata scarAdMetadata, UnityBannerSize size) {
BannerView bannerView = this.getBannerView(bannerAdId);
if (bannerView != null) {
bannerView.loadScarPlayer(bannerAdId, scarAdMetadata, size);
}
}
public synchronized void addScarContainer(String bannerAdId) {
BannerView bannerView = this.getBannerView(bannerAdId);
if (bannerView != null) {
bannerView.addScarContainer();
}
}
public synchronized boolean loadWebPlayer(String bannerAdId, UnityBannerSize size) {
BannerView bannerView = this.getBannerView(bannerAdId);
if (bannerView != null) {
@ -57,6 +89,8 @@ public class BannerViewCache {
public synchronized void triggerBannerLoadEvent(String bannerAdId) {
final BannerView bannerView = this.getBannerView(bannerAdId);
if (bannerView != null && bannerView.getListener() != null) {
LoadBannerModule.getInstance().onUnityAdsAdLoaded(bannerAdId);
final BannerView.IListener listener = bannerView.getListener();
Utilities.runOnUiThread(new Runnable() {
@Override
@ -100,7 +134,8 @@ public class BannerViewCache {
}
public synchronized void triggerBannerErrorEvent(String bannerAdId, final BannerErrorInfo bannerErrorInfo) {
LoadBannerModule.getInstance().onUnityAdsFailedToLoad(bannerAdId, UnityAds.UnityAdsLoadError.INTERNAL_ERROR, bannerErrorInfo.errorMessage);
final UnityAds.UnityAdsLoadError unityAdsLoadError = bannerErrorInfo.toLoadError();
LoadBannerModule.getInstance().onUnityAdsFailedToLoad(bannerAdId, unityAdsLoadError, bannerErrorInfo.errorMessage);
}
public synchronized void triggerBannerLeftApplicationEvent(String bannerAdId) {

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

@ -2,6 +2,7 @@ package com.unity3d.services.banners.api;
import static com.unity3d.ads.UnityAds.UnityAdsLoadError.INTERNAL_ERROR;
import com.unity3d.scar.adapter.common.scarads.ScarAdMetadata;
import com.unity3d.services.ads.operation.load.ILoadOperation;
import com.unity3d.services.ads.operation.load.LoadBannerModule;
import com.unity3d.services.ads.operation.load.LoadBannerOperationState;
@ -33,11 +34,13 @@ public class Banner {
final BannerViewType bannerViewType = BannerViewType.fromString(bannerViewTypeString);
switch (bannerViewType) {
case WEB_PLAYER:
ILoadOperation operationState = LoadBannerModule.getInstance().get(bannerAdId);
if (operationState instanceof LoadBannerOperationState) {
((LoadBannerOperationState) operationState).setSize(new UnityBannerSize(width, height));
final LoadBannerOperationState state = getBannerOperationState(bannerAdId);
if (state == null) {
break;
}
LoadBannerModule.getInstance().onUnityAdsAdLoaded(bannerAdId);
state.setSize(new UnityBannerSize(width, height));
BannerViewCache.getInstance().loadBanner(state);
break;
case UNKNOWN:
LoadBannerModule.getInstance().onUnityAdsFailedToLoad(bannerAdId, INTERNAL_ERROR, "Unknown banner type");
@ -47,6 +50,24 @@ public class Banner {
callback.invoke();
}
@WebViewExposed
public static void loadScar(final String bannerAdId, final String placementId, final String queryId, final String adUnitId, final String adString, final Integer width, final Integer height, final WebViewCallback callback) {
final LoadBannerOperationState state = getBannerOperationState(bannerAdId);
if (state == null) {
callback.invoke();
return;
}
state.setSize(new UnityBannerSize(width, height));
ScarAdMetadata scarAdMetadata = new ScarAdMetadata(placementId, queryId, adUnitId, adString, 0);
state.setScarAdMetadata(scarAdMetadata);
BannerViewCache.getInstance().loadBanner(state);
callback.invoke();
}
@WebViewExposed
public static void setRefreshRate(final String placementId, final Integer refreshRate, WebViewCallback callback) {
if (placementId != null && refreshRate != null) {
@ -55,4 +76,20 @@ public class Banner {
callback.invoke();
}
private static LoadBannerOperationState getBannerOperationState(String bannerAdId) {
ILoadOperation operationState = LoadBannerModule.getInstance().get(bannerAdId);
if (operationState == null || operationState.getLoadOperationState() == null) {
LoadBannerModule.getInstance().onUnityAdsFailedToLoad(bannerAdId, INTERNAL_ERROR, "No operation found for requested banner");
return null;
}
LoadOperationState state = operationState.getLoadOperationState();
if (state instanceof LoadBannerOperationState) {
return (LoadBannerOperationState) state;
}
LoadBannerModule.getInstance().onUnityAdsFailedToLoad(bannerAdId, INTERNAL_ERROR, "Operation state found is not for banner ad");
return null;
}
}

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

@ -104,6 +104,20 @@ public class BannerBridge {
}
}
public static void didAttachScarBanner(String bannerAdId) {
WebViewApp webViewApp = WebViewApp.getCurrentApp();
if (webViewApp != null) {
webViewApp.sendEvent(WebViewEventCategory.BANNER, BannerEvent.SCAR_BANNER_ATTACHED, bannerAdId);
}
}
public static void didDetachScarBanner(String bannerAdId) {
WebViewApp webViewApp = WebViewApp.getCurrentApp();
if (webViewApp != null) {
webViewApp.sendEvent(WebViewEventCategory.BANNER, BannerEvent.SCAR_BANNER_DETACHED, bannerAdId);
}
}
public enum BannerEvent {
BANNER_VISIBILITY_CHANGED,
BANNER_RESIZED,
@ -112,6 +126,15 @@ public class BannerBridge {
BANNER_ATTACHED,
BANNER_DETACHED,
BANNER_LOAD_PLACEMENT,
BANNER_DESTROY_BANNER
BANNER_DESTROY_BANNER,
// Used for SCAR banners only
SCAR_BANNER_LOADED,
SCAR_BANNER_LOAD_FAILED,
SCAR_BANNER_ATTACHED,
SCAR_BANNER_DETACHED,
SCAR_BANNER_OPENED,
SCAR_BANNER_CLOSED,
SCAR_BANNER_IMPRESSION,
SCAR_BANNER_CLICKED
}
}

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

@ -0,0 +1,42 @@
package com.unity3d.services.banners.view;
import android.content.Context;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;
import com.unity3d.services.banners.bridge.BannerBridge;
import com.unity3d.services.core.misc.Utilities;
public class ScarBannerContainer extends RelativeLayout {
private String _bannerAdId;
public ScarBannerContainer(Context context, String bannerAdId) {
super(context);
_bannerAdId = bannerAdId;
}
public void destroy() {
final ScarBannerContainer self = this;
Utilities.runOnUiThread(() -> {
self.removeAllViews();
ViewParent parent = self.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(self);
}
});
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
BannerBridge.didAttachScarBanner(_bannerAdId);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
BannerBridge.didDetachScarBanner(_bannerAdId);
}
}

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

@ -53,6 +53,11 @@ public class DeviceInfo {
callback.invoke(Device.getApiLevel());
}
@WebViewExposed
public static void getExtensionVersion (WebViewCallback callback) {
callback.invoke(Device.getExtensionVersion());
}
@WebViewExposed
public static void getOsVersion (WebViewCallback callback) {
callback.invoke(Device.getOsVersion());
@ -521,5 +526,20 @@ public class DeviceInfo {
public static void getBuildVersionIncremental(WebViewCallback callback) {
callback.invoke(Device.getBuildVersionIncremental());
}
@WebViewExposed
public static void hasX264HWDecoder(WebViewCallback callback) {
callback.invoke(Device.hasX264Decoder());
}
@WebViewExposed
public static void hasX265HWDecoder(WebViewCallback callback) {
callback.invoke(Device.hasX265Decoder());
}
@WebViewExposed
public static void hasAV1HWDecoder(WebViewCallback callback) {
callback.invoke(Device.hasAV1Decoder());
}
}

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

@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class Configuration {
private String _webViewUrl;
@ -49,6 +50,7 @@ public class Configuration {
private double _metricSampleRate;
private long _webViewAppCreateTimeout;
private String _scarBiddingUrl;
private Boolean _metricsEnabled;
private String _filteredJsonString;
private JSONObject _rawJsonData;
@ -276,6 +278,7 @@ public class Configuration {
_privacyRequestWaitTimeout = configData.optInt("prwto", 3000);
_src = configData.optString("src", null);
_scarBiddingUrl = configData.optString("scurl", SCAR_PRD_BIDDING_ENDPOINT);
_metricsEnabled = _metricSampleRate >= new Random().nextInt(99) + 1;
IExperiments experiments;
if (configData.has("expo")) {
@ -322,4 +325,8 @@ public class Configuration {
return filteredConfig;
}
public Boolean areMetricsEnabledForCurrentSession() {
return _metricsEnabled;
}
}

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

@ -1,5 +1,8 @@
package com.unity3d.services.core.configuration;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.startup.AppInitializer;
import com.unity3d.ads.UnityAds;
@ -106,11 +109,14 @@ public class CoreModuleConfiguration implements IModuleConfiguration {
List<Metric> metrics = new ArrayList<>();
int hasX264 = Device.hasX264Decoder() ? 1 : 0;
int hasX265 = Device.hasX265Decoder() ? 1 : 0;
int hasAV1 = Device.hasAV1Decoder() ? 1 : 0;
metrics.add(new Metric("native_device_decoder_x264", hasX264));
metrics.add(new Metric("native_device_decoder_x265", hasX265));
metrics.add(new Metric("native_device_decoder_av1", hasAV1));
SDKMetricsSender sdkMetricsSender = Utilities.getService(SDKMetricsSender.class);
sdkMetricsSender.sendMetrics(metrics);
checkForCronet(configuration);
checkForPC(configuration, sdkMetricsSender);
}
private void checkForCronet(Configuration configuration) {
@ -119,4 +125,14 @@ public class CoreModuleConfiguration implements IModuleConfiguration {
.initializeComponent(CronetInitializer.class);
}
}
private void checkForPC(Configuration configuration, SDKMetricsSender sdkMetricsSender) {
if (configuration.getExperiments().isPCCheckEnabled()) {
Context context = ClientProperties.getApplicationContext();
if (context == null) return;
PackageManager pm = context.getPackageManager();
boolean isPC = pm.hasSystemFeature("com.google.android.play.feature.HPE_EXPERIENCE");
sdkMetricsSender.sendMetric(new Metric("native_device_is_pc", isPC ? 1 : 0));
}
}
}

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

@ -1,7 +1,5 @@
package com.unity3d.services.core.configuration;
import com.unity3d.services.ads.gmascar.managers.SCARBiddingManagerType;
import org.json.JSONObject;
import java.util.HashMap;
@ -53,11 +51,6 @@ public class ExperimentObjects extends ExperimentsBase {
return getExperimentValueOrDefault(EXP_TAG_SCAR_INIT);
}
@Override
public String getScarBiddingManager() {
return getExperimentValue(EXP_TAG_SCAR_BIDDING_MANAGER, SCARBiddingManagerType.DISABLED.getName());
}
@Override
public boolean isJetpackLifecycle() {
return getExperimentValueOrDefault(EXP_TAG_JETPACK_LIFECYCLE);
@ -83,6 +76,27 @@ public class ExperimentObjects extends ExperimentsBase {
return getExperimentValueOrDefault(EXP_TAG_CRONET_CHECK);
}
@Override
public boolean isNativeShowTimeoutDisabled() {
return getExperimentValueOrDefault(EXP_TAG_SHOW_TIMEOUT_DISABLED);
}
@Override
public boolean isNativeLoadTimeoutDisabled() {
return getExperimentValueOrDefault(EXP_TAG_LOAD_TIMEOUT_DISABLED);
}
@Override
public boolean isCaptureHDRCapabilitiesEnabled() {
return getExperimentValueOrDefault(EXP_TAG_HDR_CAPABILITIES);
}
@Override
public boolean isScarBannerHbEnabled() { return getExperimentValueOrDefault(EXP_TAG_SCAR_HB_BN); }
@Override
public boolean isPCCheckEnabled() { return getExperimentValueOrDefault(EXP_TAG_IS_PC_CHECK_ENABLED); }
private String getExperimentValue(String experimentName, String defaultValue) {
ExperimentObject expo = getExperimentObject(experimentName);
return (expo != null) ? expo.getStringValue() : defaultValue;

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

@ -1,10 +1,7 @@
package com.unity3d.services.core.configuration;
import com.unity3d.services.ads.gmascar.managers.SCARBiddingManagerType;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -56,8 +53,8 @@ public class Experiments extends ExperimentsBase {
}
@Override
public String getScarBiddingManager() {
return _experimentData.optString(EXP_TAG_SCAR_BIDDING_MANAGER, SCARBiddingManagerType.DISABLED.getName());
public boolean isScarBannerHbEnabled() {
return _experimentData.optBoolean(EXP_TAG_SCAR_HB_BN, false);
}
@Override
@ -85,6 +82,26 @@ public class Experiments extends ExperimentsBase {
return _experimentData.optBoolean(EXP_TAG_CRONET_CHECK, false);
}
@Override
public boolean isNativeShowTimeoutDisabled() {
return _experimentData.optBoolean(EXP_TAG_SHOW_TIMEOUT_DISABLED, false);
}
@Override
public boolean isNativeLoadTimeoutDisabled() {
return _experimentData.optBoolean(EXP_TAG_LOAD_TIMEOUT_DISABLED, false);
}
@Override
public boolean isCaptureHDRCapabilitiesEnabled() {
return _experimentData.optBoolean(EXP_TAG_HDR_CAPABILITIES, false);
}
@Override
public boolean isPCCheckEnabled() {
return _experimentData.optBoolean(EXP_TAG_IS_PC_CHECK_ENABLED, false);
}
public JSONObject getExperimentsAsJson() {
return _experimentData;
}

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

@ -6,12 +6,15 @@ public abstract class ExperimentsBase implements IExperiments {
static final String EXP_TAG_WEB_AD_ASSET_CACHING = "wac";
static final String EXP_TAG_WEB_GESTURE_NOT_REQUIRED = "wgr";
static final String EXP_TAG_SCAR_INIT = "scar_init";
static final String EXP_TAG_SCAR_BIDDING_MANAGER = "scar_bm";
static final String EXP_TAG_JETPACK_LIFECYCLE = "gjl";
static final String EXP_TAG_OK_HTTP = "okhttp";
static final String EXP_TAG_WEB_MESSAGE = "jwm";
static final String EXP_TAG_WEBVIEW_ASYNC_DOWNLOAD = "wad";
static final String EXP_TAG_CRONET_CHECK = "cce";
static final String EXP_TAG_SHOW_TIMEOUT_DISABLED = "nstd";
static final String EXP_TAG_LOAD_TIMEOUT_DISABLED = "nltd";
static final String EXP_TAG_HDR_CAPABILITIES = "hdrc";
static final String EXP_TAG_SCAR_HB_BN = "scar_bn";
static final String EXP_TAG_IS_PC_CHECK_ENABLED = "pc_check";
static final boolean EXP_DEFAULT_VALUE = false;
}

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

@ -10,12 +10,16 @@ public interface IExperiments {
boolean isWebAssetAdCaching();
boolean isWebGestureNotRequired();
boolean isScarInitEnabled();
String getScarBiddingManager();
boolean isJetpackLifecycle();
boolean isOkHttpEnabled();
boolean isWebMessageEnabled();
boolean isWebViewAsyncDownloadEnabled();
boolean isCronetCheckEnabled();
boolean isNativeShowTimeoutDisabled();
boolean isNativeLoadTimeoutDisabled();
boolean isCaptureHDRCapabilitiesEnabled();
boolean isScarBannerHbEnabled();
boolean isPCCheckEnabled();
JSONObject getCurrentSessionExperiments();
JSONObject getNextSessionExperiments();

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

@ -1,5 +1,6 @@
package com.unity3d.services.core.device;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
@ -19,6 +20,7 @@ import android.net.NetworkInfo;
import android.os.BatteryManager;
import android.os.Build;
import android.os.SystemClock;
import android.os.ext.SdkExtensions;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@ -61,6 +63,14 @@ public class Device {
return Build.VERSION.SDK_INT;
}
public static int getExtensionVersion() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R);
} else {
return -1;
}
}
public static String getOsVersion() {
return Build.VERSION.RELEASE;
}
@ -699,6 +709,10 @@ public class Device {
return Device.selectAllDecodeCodecs(MimeTypes.VIDEO_H265).size() > 0;
}
public static boolean hasAV1Decoder() {
return Device.selectAllDecodeCodecs(MimeTypes.VIDEO_AV1).size() > 0;
}
public static List<MediaCodecInfo> selectAllDecodeCodecs(String mimeType) {
List<MediaCodecInfo> result = new ArrayList<>();
int numCodecs = MediaCodecList.getCodecCount();

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

@ -7,4 +7,5 @@ public class MimeTypes {
public static final String VIDEO_WEBM = BASE_TYPE_VIDEO + "/webm";
public static final String VIDEO_H264 = BASE_TYPE_VIDEO + "/avc";
public static final String VIDEO_H265 = BASE_TYPE_VIDEO + "/hevc";
public static final String VIDEO_AV1 = BASE_TYPE_VIDEO + "/av01";
}

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

@ -0,0 +1,106 @@
package com.unity3d.services.core.device.reader;
import static android.view.Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION;
import static android.view.Display.HdrCapabilities.HDR_TYPE_HDR10;
import static android.view.Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS;
import static android.view.Display.HdrCapabilities.HDR_TYPE_HLG;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.view.Display;
import android.view.WindowManager;
import com.unity3d.services.core.configuration.ExperimentsReader;
import com.unity3d.services.core.misc.Utilities;
import com.unity3d.services.core.request.metrics.Metric;
import com.unity3d.services.core.request.metrics.SDKMetricsSender;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class HdrInfoReader implements IHdrInfoReader {
private final SDKMetricsSender _sdkMetricsSender = Utilities.getService(SDKMetricsSender.class);
private static final AtomicBoolean _hdrMetricsCaptured = new AtomicBoolean(false);
private static volatile HdrInfoReader _instance;
private HdrInfoReader() {}
public static HdrInfoReader getInstance() {
if (_instance == null) {
synchronized (HdrInfoReader.class) {
if (_instance == null) {
_instance = new HdrInfoReader();
}
}
}
return _instance;
}
@Override
public void captureHDRCapabilityMetrics(Activity activity, ExperimentsReader experimentsReader) {
if (activity == null) return;
if (!experimentsReader.getCurrentlyActiveExperiments().isCaptureHDRCapabilitiesEnabled()) return;
if (_hdrMetricsCaptured.compareAndSet(false, true)) {
List<Metric> hdrMetrics = new ArrayList<>(5);
int hasDolbyVision = 0;
int hasHDR10 = 0;
int hasHDR10Plus = 0;
int hasHLG = 0;
int isScreenHDR = 0;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Display display = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
int[] types = hdrCapabilities.getSupportedHdrTypes();
for (int type : types) {
switch (type) {
case HDR_TYPE_DOLBY_VISION:
hasDolbyVision = 1;
break;
case HDR_TYPE_HDR10:
hasHDR10 = 1;
break;
case HDR_TYPE_HLG:
hasHLG = 1;
break;
case HDR_TYPE_HDR10_PLUS:
hasHDR10Plus = 1;
break;
}
}
long maxAverage = Math.round(hdrCapabilities.getDesiredMaxAverageLuminance());
long maxLum = Math.round(hdrCapabilities.getDesiredMaxLuminance());
long minLum = Math.round(hdrCapabilities.getDesiredMinLuminance());
hdrMetrics.add(new Metric("native_device_hdr_lum_max_average", maxAverage));
hdrMetrics.add(new Metric("native_device_hdr_lum_max", maxLum));
hdrMetrics.add(new Metric("native_device_hdr_lum_min", minLum));
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Configuration configuration = activity.getResources().getConfiguration();
isScreenHDR = configuration.isScreenHdr() ? 1 : 0;
}
}
hdrMetrics.add(new Metric("native_device_hdr_dolby_vision", hasDolbyVision));
hdrMetrics.add(new Metric("native_device_hdr_hdr10", hasHDR10));
hdrMetrics.add(new Metric("native_device_hdr_hdr10_plus", hasHDR10Plus));
hdrMetrics.add(new Metric("native_device_hdr_hlg", hasHLG));
hdrMetrics.add(new Metric("native_device_hdr_screen_hdr", isScreenHDR));
_sdkMetricsSender.sendMetrics(hdrMetrics);
}
}
}

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

@ -0,0 +1,9 @@
package com.unity3d.services.core.device.reader;
import android.app.Activity;
import com.unity3d.services.core.configuration.ExperimentsReader;
public interface IHdrInfoReader {
void captureHDRCapabilityMetrics(Activity activity, ExperimentsReader experimentsReader);
}

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

@ -3,10 +3,12 @@ package com.unity3d.services.core.di
import android.os.Handler
import android.os.Looper
import com.unity3d.services.SDKErrorHandler
import com.unity3d.services.ads.measurements.MeasurementsService
import com.unity3d.services.ads.token.AsyncTokenStorage
import com.unity3d.services.ads.token.InMemoryAsyncTokenStorage
import com.unity3d.services.ads.token.InMemoryTokenStorage
import com.unity3d.services.ads.token.TokenStorage
import com.unity3d.services.ads.topics.TopicsService
import com.unity3d.services.core.device.VolumeChange
import com.unity3d.services.core.device.VolumeChangeContentObserver
import com.unity3d.services.core.device.VolumeChangeMonitor
@ -15,6 +17,7 @@ import com.unity3d.services.core.domain.SDKDispatchers
import com.unity3d.services.core.domain.task.*
import com.unity3d.services.core.network.core.LegacyHttpClient
import com.unity3d.services.core.network.core.HttpClient
import com.unity3d.services.core.properties.ClientProperties
import com.unity3d.services.core.network.core.OkHttp3Client
import com.unity3d.services.core.request.metrics.SDKMetrics
import com.unity3d.services.core.request.metrics.SDKMetricsSender
@ -84,6 +87,9 @@ object ServiceProvider : IServiceProvider {
single<VolumeChange> { VolumeChangeContentObserver() }
single { VolumeChangeMonitor(SharedInstances.webViewEventSender, get()) }
// android privacy sandbox
single { MeasurementsService(ClientProperties.getApplicationContext(), get(), SharedInstances.webViewEventSender) }
single { TopicsService(ClientProperties.getApplicationContext(), get(), SharedInstances.webViewEventSender) }
}
/**
@ -125,7 +131,7 @@ object ServiceProvider : IServiceProvider {
*/
private fun provideHttpClient(dispatchers: ISDKDispatchers, configFileFromLocalStorage: ConfigFileFromLocalStorage): HttpClient {
val config = runBlocking {
runCatching { configFileFromLocalStorage(ConfigFileFromLocalStorage.Params()) }.getOrNull()
configFileFromLocalStorage(ConfigFileFromLocalStorage.Params()).getOrNull()
}
if (config?.experiments?.isOkHttpEnabled == true) {

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

@ -8,8 +8,8 @@ import com.unity3d.services.core.di.IServiceComponent
*/
interface BaseTask<in P: BaseParams, R>: IServiceComponent {
suspend operator fun invoke(params: P): R = doWork(params)
suspend operator fun invoke(params: P): Result<R> = doWork(params)
suspend fun doWork(params: P): R
suspend fun doWork(params: P): Result<R>
}

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

@ -2,6 +2,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.properties.SdkProperties
import kotlinx.coroutines.withContext
import org.json.JSONObject
@ -20,12 +21,14 @@ class ConfigFileFromLocalStorage(
return getMetricNameForInitializeTask("read_local_config")
}
override suspend fun doWork(params: Params): Configuration =
override suspend fun doWork(params: Params): Result<Configuration> =
withContext(dispatchers.io) {
val configFile = File(SdkProperties.getLocalConfigurationFilepath())
val fileContent = configFile.readText()
val loadedJson = JSONObject(fileContent)
Configuration(loadedJson)
runReturnSuspendCatching {
val configFile = File(SdkProperties.getLocalConfigurationFilepath())
val fileContent = configFile.readText()
val loadedJson = JSONObject(fileContent)
Configuration(loadedJson)
}
}
/**

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

@ -5,6 +5,7 @@ import com.unity3d.services.core.configuration.ErrorState
import com.unity3d.services.core.configuration.InitializeEventsMetricSender
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.domain.getInitializationExceptionOrThrow
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.lifecycle.CachedLifecycle
import com.unity3d.services.core.log.DeviceLog
import kotlinx.coroutines.CoroutineName
@ -31,80 +32,82 @@ class InitializeSDK(
return getMetricNameForInitializeTask("initialize")
}
override suspend fun doWork(params: EmptyParams) =
override suspend fun doWork(params: EmptyParams) : Result<Unit> =
withContext(dispatchers.default) {
InitializeEventsMetricSender.getInstance().didInitStart()
CachedLifecycle.register()
runReturnSuspendCatching {
InitializeEventsMetricSender.getInstance().didInitStart()
CachedLifecycle.register()
// check if we have a configuration cached
DeviceLog.debug("Unity Ads Init: Loading Config File From Local Storage")
val configuration = runCatching { configFileFromLocalStorage(ConfigFileFromLocalStorage.Params()) }
.onFailure { DeviceLog.debug("Unity Ads Init: Could not load config file from local storage: ${it.message}") }
.getOrDefault(Configuration())
// check if we have a configuration cached
DeviceLog.debug("Unity Ads Init: Loading Config File From Local Storage")
val configuration = configFileFromLocalStorage(ConfigFileFromLocalStorage.Params())
.onFailure { DeviceLog.debug("Unity Ads Init: Could not load config file from local storage: ${it.message}") }
.getOrDefault(Configuration())
// reset modules
val resetResult = runCatching { initializeStateReset(InitializeStateReset.Params(configuration)) }
if (resetResult.isFailure) {
executeErrorState(ErrorState.ResetWebApp, resetResult.exceptionOrNull(), configuration)
throw resetResult.exceptionOrNull() ?: Exception(ErrorState.ResetWebApp.toString())
}
// reset modules
val resetResult = initializeStateReset(InitializeStateReset.Params(configuration))
if (resetResult.isFailure) {
executeErrorState(ErrorState.ResetWebApp, resetResult.exceptionOrNull(), configuration)
throw resetResult.exceptionOrNull() ?: Exception(ErrorState.ResetWebApp.toString())
}
// get native config
val configResult = runCatching { initializeStateConfig(InitializeStateConfig.Params(configuration)) }
if (configResult.isFailure) {
handleInitializationException(configResult.getInitializationExceptionOrThrow())
}
// get native config
val configResult = initializeStateConfig(InitializeStateConfig.Params(configuration))
if (configResult.isFailure) {
handleInitializationException(configResult.getInitializationExceptionOrThrow())
}
if (configuration.experiments.isNativeWebViewCacheEnabled) {
// load webview from remote url
val createWithRemoteResult = runCatching {
initializeStateCreateWithRemote(
InitializeStateCreateWithRemote.Params(configResult.getOrThrow())
if (configuration.experiments.isNativeWebViewCacheEnabled) {
// load webview from remote url
val createWithRemoteResult = initializeStateCreateWithRemote(
InitializeStateCreateWithRemote.Params(configResult.getOrThrow())
)
}
if (createWithRemoteResult.isSuccess) {
initializeStateComplete(InitializeStateComplete.Params(configResult.getOrThrow()))
return@withContext
} else {
handleInitializationException(createWithRemoteResult.getInitializationExceptionOrThrow())
}
}
// load WebView from cache if available and correct, if not fetch and load
val loadCacheResult =
runCatching { initializeStateLoadCache(InitializeStateLoadCache.Params(configResult.getOrThrow())) }
if (loadCacheResult.isFailure) {
executeErrorState(ErrorState.LoadCache, loadCacheResult.exceptionOrNull(), configuration)
throw loadCacheResult.exceptionOrNull() ?: Exception(ErrorState.LoadCache.toString())
}
val loadCacheResultData = loadCacheResult.getOrThrow()
val webViewData: String = if (loadCacheResultData.hasHashMismatch) {
// cached data has mismatch on checksum
if (configuration.experiments.isWebViewAsyncDownloadEnabled && loadCacheResultData.webViewData != null) {
// Fire and forget webView download, will be used in next session.
launch(CoroutineName("LaunchLoadWeb")) { runCatching { initializeStateLoadWeb(InitializeStateLoadWeb.Params(configResult.getOrThrow())) }}
loadCacheResultData.webViewData
} else {
// We don't have any cached WebView, we must wait for download to complete to continue init
val loadWebResult = runCatching { initializeStateLoadWeb(InitializeStateLoadWeb.Params(configResult.getOrThrow())) }
if (loadWebResult.isFailure) {
handleInitializationException(loadWebResult.getInitializationExceptionOrThrow())
if (createWithRemoteResult.isSuccess) {
initializeStateComplete(InitializeStateComplete.Params(configResult.getOrThrow())).getOrThrow()
return@runReturnSuspendCatching
} else {
handleInitializationException(createWithRemoteResult.getInitializationExceptionOrThrow())
}
loadWebResult.getOrThrow().webViewDataString
}
} else {
checkNotNull(loadCacheResultData.webViewData) { "WebView is missing." }
}
val createResult =
runCatching { initializeStateCreate(InitializeStateCreate.Params(configResult.getOrThrow(), webViewData)) }
if (createResult.isFailure) {
handleInitializationException(createResult.getInitializationExceptionOrThrow())
}
// load WebView from cache if available and correct, if not fetch and load
val loadCacheResult = initializeStateLoadCache(InitializeStateLoadCache.Params(configResult.getOrThrow()))
initializeStateComplete(InitializeStateComplete.Params(configResult.getOrThrow()))
return@withContext
if (loadCacheResult.isFailure) {
executeErrorState(ErrorState.LoadCache, loadCacheResult.exceptionOrNull(), configuration)
throw loadCacheResult.exceptionOrNull() ?: Exception(ErrorState.LoadCache.toString())
}
val loadCacheResultData = loadCacheResult.getOrThrow()
val webViewData: String = if (loadCacheResultData.hasHashMismatch) {
// cached data has mismatch on checksum
if (configuration.experiments.isWebViewAsyncDownloadEnabled && loadCacheResultData.webViewData != null) {
// Fire and forget webView download, will be used in next session.
launch(CoroutineName("LaunchLoadWeb")) {
initializeStateLoadWeb(InitializeStateLoadWeb.Params(configResult.getOrThrow()))
}
loadCacheResultData.webViewData
} else {
// We don't have any cached WebView, we must wait for download to complete to continue init
val loadWebResult = initializeStateLoadWeb(InitializeStateLoadWeb.Params(configResult.getOrThrow()))
if (loadWebResult.isFailure) {
handleInitializationException(loadWebResult.getInitializationExceptionOrThrow())
}
loadWebResult.getOrThrow().webViewDataString
}
} else {
checkNotNull(loadCacheResultData.webViewData) { "WebView is missing." }
}
val createResult = initializeStateCreate(InitializeStateCreate.Params(configResult.getOrThrow(), webViewData))
if (createResult.isFailure) {
handleInitializationException(createResult.getInitializationExceptionOrThrow())
}
initializeStateComplete(InitializeStateComplete.Params(configResult.getOrThrow())).getOrThrow()
}
}
private suspend fun handleInitializationException(exception: InitializationException) {

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

@ -2,6 +2,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import kotlinx.coroutines.withContext
class InitializeStateComplete(
@ -12,9 +13,11 @@ class InitializeStateComplete(
return getMetricNameForInitializeTask("completion")
}
override suspend fun doWork(params: Params): Unit = withContext(dispatchers.default) {
for (moduleName in params.config.moduleConfigurationList) {
params.config.getModuleConfiguration(moduleName)?.initCompleteState(params.config)
override suspend fun doWork(params: Params): Result<Unit> = withContext(dispatchers.default) {
runReturnSuspendCatching {
for (moduleName in params.config.moduleConfigurationList) {
params.config.getModuleConfiguration(moduleName)?.initCompleteState(params.config)
}
}
}

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

@ -2,6 +2,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.properties.SdkProperties
import kotlinx.coroutines.withContext
@ -15,15 +16,17 @@ class InitializeStateConfig(
return getMetricNameForInitializeTask("config_fetch")
}
override suspend fun doWork(params: Params): Configuration =
override suspend fun doWork(params: Params): Result<Configuration> =
withContext(dispatchers.default) {
DeviceLog.info("Unity Ads init: load configuration from " + SdkProperties.getConfigUrl())
val configuration = Configuration(
SdkProperties.getConfigUrl(),
params.config.experimentsReader
)
runReturnSuspendCatching {
DeviceLog.info("Unity Ads init: load configuration from " + SdkProperties.getConfigUrl())
val configuration = Configuration(
SdkProperties.getConfigUrl(),
params.config.experimentsReader
)
initializeStateConfigWithLoader(InitializeStateConfigWithLoader.Params(configuration))
initializeStateConfigWithLoader(InitializeStateConfigWithLoader.Params(configuration)).getOrThrow()
}
}
data class Params(val config: Configuration) : BaseParams

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

@ -6,8 +6,8 @@ import com.unity3d.services.core.device.reader.DeviceInfoDataFactory
import com.unity3d.services.core.di.get
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.AbortRetryException
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.extensions.withRetry
import com.unity3d.services.core.request.metrics.SDKMetrics
import com.unity3d.services.core.request.metrics.SDKMetricsSender
import com.unity3d.services.core.request.metrics.TSIMetric
import kotlinx.coroutines.withContext
@ -27,105 +27,114 @@ class InitializeStateConfigWithLoader(
private val sdkMetricsSender: SDKMetricsSender
) : BaseTask<InitializeStateConfigWithLoader.Params, Configuration> {
override suspend fun doWork(params: Params) =
override suspend fun doWork(params: Params) : Result<Configuration> =
withContext(dispatchers.default) {
val privacyConfigStorage = PrivacyConfigStorage.getInstance()
val deviceInfoDataFactory = DeviceInfoDataFactory()
runReturnSuspendCatching {
val privacyConfigStorage = PrivacyConfigStorage.getInstance()
val deviceInfoDataFactory = DeviceInfoDataFactory()
var configurationLoader: IConfigurationLoader = ConfigurationLoader(
ConfigurationRequestFactory(
params.config,
deviceInfoDataFactory.getDeviceInfoData(InitRequestType.TOKEN)
), get()
)
configurationLoader = PrivacyConfigurationLoader(
configurationLoader,
ConfigurationRequestFactory(
params.config,
deviceInfoDataFactory.getDeviceInfoData(InitRequestType.PRIVACY)
),
privacyConfigStorage
)
var config = Configuration()
val configResult = runCatching {
withRetry(
retries = params.config.maxRetries,
scalingFactor = params.config.retryScalingFactor,
retryDelay = params.config.retryDelay,
fallbackException = InitializationException(
ErrorState.NetworkConfigRequest,
Exception(),
params.config
var configurationLoader: IConfigurationLoader = ConfigurationLoader(
ConfigurationRequestFactory(
params.config,
deviceInfoDataFactory.getDeviceInfoData(InitRequestType.TOKEN)
), get()
)
configurationLoader = PrivacyConfigurationLoader(
configurationLoader,
ConfigurationRequestFactory(
params.config,
deviceInfoDataFactory.getDeviceInfoData(InitRequestType.PRIVACY)
),
) {
if (it > 0) InitializeEventsMetricSender.getInstance().onRetryConfig()
withContext(dispatchers.io) {
configurationLoader.loadConfiguration(object :
IConfigurationLoaderListener {
override fun onSuccess(configuration: Configuration) {
config = configuration
config.saveToDisk()
tokenStorage.setInitToken(config.unifiedAuctionToken)
}
privacyConfigStorage
)
var config = Configuration()
override fun onError(errorMsg: String) {
sdkMetricsSender.sendMetric(TSIMetric.newEmergencySwitchOff())
// Shall return with error and stop retries
throw AbortRetryException(errorMsg)
}
})
val configResult = runCatching {
withRetry(
retries = params.config.maxRetries,
scalingFactor = params.config.retryScalingFactor,
retryDelay = params.config.retryDelay,
fallbackException = InitializationException(
ErrorState.NetworkConfigRequest,
Exception(),
params.config
),
) {
if (it > 0) InitializeEventsMetricSender.getInstance().onRetryConfig()
withContext(dispatchers.io) {
configurationLoader.loadConfiguration(object :
IConfigurationLoaderListener {
override fun onSuccess(configuration: Configuration) {
config = configuration
config.saveToDisk()
tokenStorage.setInitToken(config.unifiedAuctionToken)
}
override fun onError(errorMsg: String) {
sdkMetricsSender.sendMetric(TSIMetric.newEmergencySwitchOff())
// Shall return with error and stop retries
throw AbortRetryException(errorMsg)
}
})
}
}
}
}
config = if (configResult.isFailure) {
// Check if it as an aborted exception
val configResultException = configResult.exceptionOrNull()
if (configResultException is AbortRetryException) {
// Abort init!
throw InitializationException(
ErrorState.NetworkConfigRequest,
configResultException,
params.config
)
}
val haveNetwork =
runCatching { initializeStateNetworkError(InitializeStateNetworkError.Params(params.config)) }
if (haveNetwork.isSuccess) {
InitializeEventsMetricSender.getInstance().onRetryConfig()
withContext(dispatchers.io) {
configurationLoader.loadConfiguration(object : IConfigurationLoaderListener {
override fun onSuccess(configuration: Configuration) {
config = configuration
config.saveToDisk()
tokenStorage.setInitToken(config.unifiedAuctionToken)
}
override fun onError(errorMsg: String) {
sdkMetricsSender.sendMetric(TSIMetric.newEmergencySwitchOff())
// Shall return with an error
throw InitializationException(
ErrorState.NetworkConfigRequest,
Exception(errorMsg),
config = if (configResult.isFailure) {
// Check if it as an aborted exception
val configResultException = configResult.exceptionOrNull()
if (configResultException is AbortRetryException) {
// Abort init!
throw InitializationException(
ErrorState.NetworkConfigRequest,
configResultException,
params.config
)
}
val haveNetwork =
runCatching {
initializeStateNetworkError(
InitializeStateNetworkError.Params(
params.config
)
}
})
)
}
if (haveNetwork.isSuccess) {
InitializeEventsMetricSender.getInstance().onRetryConfig()
withContext(dispatchers.io) {
configurationLoader.loadConfiguration(object :
IConfigurationLoaderListener {
override fun onSuccess(configuration: Configuration) {
config = configuration
config.saveToDisk()
tokenStorage.setInitToken(config.unifiedAuctionToken)
}
override fun onError(errorMsg: String) {
sdkMetricsSender.sendMetric(TSIMetric.newEmergencySwitchOff())
// Shall return with an error
throw InitializationException(
ErrorState.NetworkConfigRequest,
Exception(errorMsg),
params.config
)
}
})
}
config
} else {
throw InitializationException(
ErrorState.NetworkConfigRequest,
Exception("No connected events within the timeout!"),
params.config
)
}
config
} else {
throw InitializationException(
ErrorState.NetworkConfigRequest,
Exception("No connected events within the timeout!"),
params.config
)
config
}
} else {
config
}
config
}
/**

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

@ -3,6 +3,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.configuration.ErrorState
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.webview.WebViewApp
import kotlinx.coroutines.withContext
@ -22,28 +23,34 @@ class InitializeStateCreate(
return getMetricNameForInitializeTask("create_web_view")
}
override suspend fun doWork(params: Params): Configuration = withContext(dispatchers.default) {
DeviceLog.debug("Unity Ads init: creating webapp")
override suspend fun doWork(params: Params): Result<Configuration> = withContext(dispatchers.default) {
runReturnSuspendCatching {
DeviceLog.debug("Unity Ads init: creating webapp")
val configuration: Configuration = params.config
configuration.webViewData = params.webViewData
val configuration: Configuration = params.config
configuration.webViewData = params.webViewData
val createErrorState: ErrorState? = try {
WebViewApp.create(configuration, false)
} catch (e: IllegalThreadStateException) {
DeviceLog.exception("Illegal Thread", e)
throw InitializationException(ErrorState.CreateWebApp, e, configuration)
}
if (createErrorState == null) {
configuration
} else {
var errorMessage: String? = "Unity Ads WebApp creation failed"
if (WebViewApp.getCurrentApp().webAppFailureMessage != null) {
errorMessage = WebViewApp.getCurrentApp().webAppFailureMessage
val createErrorState: ErrorState? = try {
WebViewApp.create(configuration, false)
} catch (e: IllegalThreadStateException) {
DeviceLog.exception("Illegal Thread", e)
throw InitializationException(ErrorState.CreateWebApp, e, configuration)
}
if (createErrorState == null) {
configuration
} else {
var errorMessage: String? = "Unity Ads WebApp creation failed"
if (WebViewApp.getCurrentApp().webAppFailureMessage != null) {
errorMessage = WebViewApp.getCurrentApp().webAppFailureMessage
}
DeviceLog.error(errorMessage)
throw InitializationException(
createErrorState,
Exception(errorMessage),
configuration
)
}
DeviceLog.error(errorMessage)
throw InitializationException(createErrorState, Exception(errorMessage), configuration)
}
}

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

@ -3,6 +3,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.configuration.ErrorState
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.webview.WebViewApp
import kotlinx.coroutines.withContext
@ -22,28 +23,34 @@ class InitializeStateCreateWithRemote(
return getMetricNameForInitializeTask("create_web_view")
}
override suspend fun doWork(params: Params): Configuration =
override suspend fun doWork(params: Params): Result<Configuration> =
withContext(dispatchers.default) {
DeviceLog.debug("Unity Ads init: creating webapp")
runReturnSuspendCatching {
DeviceLog.debug("Unity Ads init: creating webapp")
val configuration: Configuration = params.config
val configuration: Configuration = params.config
val createErrorState: ErrorState? = try {
WebViewApp.create(configuration, true)
} catch (e: IllegalThreadStateException) {
DeviceLog.exception("Illegal Thread", e)
throw InitializationException(ErrorState.CreateWebApp, e, configuration)
}
if (createErrorState == null) {
configuration
} else {
var errorMessage: String? = "Unity Ads WebApp creation failed"
if (WebViewApp.getCurrentApp().webAppFailureMessage != null) {
errorMessage = WebViewApp.getCurrentApp().webAppFailureMessage
val createErrorState: ErrorState? = try {
WebViewApp.create(configuration, true)
} catch (e: IllegalThreadStateException) {
DeviceLog.exception("Illegal Thread", e)
throw InitializationException(ErrorState.CreateWebApp, e, configuration)
}
if (createErrorState == null) {
configuration
} else {
var errorMessage: String? = "Unity Ads WebApp creation failed"
if (WebViewApp.getCurrentApp().webAppFailureMessage != null) {
errorMessage = WebViewApp.getCurrentApp().webAppFailureMessage
}
DeviceLog.error(errorMessage)
throw InitializationException(
createErrorState,
Exception(errorMessage),
configuration
)
}
DeviceLog.error(errorMessage)
throw InitializationException(createErrorState, Exception(errorMessage), configuration)
}
}

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

@ -3,6 +3,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.configuration.ErrorState
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import kotlinx.coroutines.withContext
@ -22,14 +23,16 @@ class InitializeStateError(
override suspend fun doWork(params: Params) =
withContext(dispatchers.default) {
DeviceLog.error("Unity Ads init: halting init in " + params.errorState.metricName + ": " + params.exception.message)
runReturnSuspendCatching {
DeviceLog.error("Unity Ads init: halting init in " + params.errorState.metricName + ": " + params.exception.message)
for (moduleName in params.config.moduleConfigurationList ?: emptyArray()) {
params.config.getModuleConfiguration(moduleName)?.initErrorState(
params.config,
params.errorState,
params.exception.message
)
for (moduleName in params.config.moduleConfigurationList ?: emptyArray()) {
params.config.getModuleConfiguration(moduleName)?.initErrorState(
params.config,
params.errorState,
params.exception.message
)
}
}
}

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

@ -2,6 +2,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.misc.Utilities
import com.unity3d.services.core.properties.SdkProperties
@ -25,23 +26,27 @@ class InitializeStateLoadCache(
return getMetricNameForInitializeTask("read_local_webview")
}
override suspend fun doWork(params: Params): LoadCacheResult =
override suspend fun doWork(params: Params): Result<LoadCacheResult> =
withContext(dispatchers.default) {
DeviceLog.debug("Unity Ads init: check if webapp can be loaded from local cache")
runReturnSuspendCatching {
DeviceLog.debug("Unity Ads init: check if webapp can be loaded from local cache")
val localWebViewData: ByteArray = getWebViewData() ?: return@withContext LoadCacheResult(hasHashMismatch = true)
val localWebViewHash: String? = Utilities.Sha256(localWebViewData)
val localWebViewData: ByteArray =
getWebViewData() ?: return@runReturnSuspendCatching LoadCacheResult(hasHashMismatch = true)
val localWebViewHash: String? = Utilities.Sha256(localWebViewData)
// If charset isn't supported on device, might throw an exception (result.isFailure)
val webViewDataString = String(localWebViewData, Charset.forName("UTF-8"))
// If charset isn't supported on device, might throw an exception (result.isFailure)
val webViewDataString = String(localWebViewData, Charset.forName("UTF-8"))
val hashMismatch = localWebViewHash == null || localWebViewHash != params.config.webViewHash
val hashMismatch =
localWebViewHash == null || localWebViewHash != params.config.webViewHash
if (!hashMismatch) {
DeviceLog.info("Unity Ads init: webapp loaded from local cache")
if (!hashMismatch) {
DeviceLog.info("Unity Ads init: webapp loaded from local cache")
}
LoadCacheResult(hashMismatch, webViewDataString)
}
LoadCacheResult(hashMismatch, webViewDataString)
}
/**

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

@ -5,6 +5,7 @@ import com.unity3d.services.core.configuration.ErrorState
import com.unity3d.services.core.configuration.InitializeEventsMetricSender
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.domain.task.InitializeStateLoadWeb.LoadWebResult
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.extensions.withRetry
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.misc.Utilities
@ -33,53 +34,65 @@ class InitializeStateLoadWeb(
return getMetricNameForInitializeTask("download_web_view")
}
override suspend fun doWork(params: Params): LoadWebResult = withContext(dispatchers.default) {
DeviceLog.info("Unity Ads init: loading webapp from " + params.config.webViewUrl)
override suspend fun doWork(params: Params): Result<LoadWebResult> = withContext(dispatchers.default) {
runReturnSuspendCatching {
DeviceLog.info("Unity Ads init: loading webapp from " + params.config.webViewUrl)
val request = HttpRequest(baseURL = params.config.webViewUrl, method = RequestType.GET)
val request = HttpRequest(baseURL = params.config.webViewUrl, method = RequestType.GET)
val webViewDataResult = runCatching {
withRetry(
retries = params.config.maxRetries,
scalingFactor = params.config.retryScalingFactor,
retryDelay = params.config.retryDelay,
fallbackException = InitializationException(
ErrorState.NetworkWebviewRequest,
Exception(),
params.config
),
) {
if (it > 0) InitializeEventsMetricSender.getInstance().onRetryWebview()
withContext(dispatchers.io) { httpClient.execute(request) }
val webViewDataResult = runCatching {
withRetry(
retries = params.config.maxRetries,
scalingFactor = params.config.retryScalingFactor,
retryDelay = params.config.retryDelay,
fallbackException = InitializationException(
ErrorState.NetworkWebviewRequest,
Exception(),
params.config
),
) {
if (it > 0) InitializeEventsMetricSender.getInstance().onRetryWebview()
withContext(dispatchers.io) { httpClient.execute(request) }
}
}
}
val webViewData: String = if (webViewDataResult.isFailure) {
val haveNetwork =
runCatching { initializeStateNetworkError(InitializeStateNetworkError.Params(params.config)) }
if (haveNetwork.isSuccess) {
withContext(dispatchers.io) { httpClient.execute(request).body.toString() }
val webViewData: String = if (webViewDataResult.isFailure) {
val haveNetwork =
runCatching {
initializeStateNetworkError(
InitializeStateNetworkError.Params(
params.config
)
)
}
if (haveNetwork.isSuccess) {
withContext(dispatchers.io) { httpClient.execute(request).body.toString() }
} else {
throw InitializationException(
ErrorState.NetworkWebviewRequest,
Exception("No connected events within the timeout!"),
params.config
)
}
} else {
webViewDataResult.getOrThrow().body.toString()
}
val webViewHash: String? = params.config.webViewHash
if (webViewHash != null && Utilities.Sha256(webViewData) != webViewHash) {
throw InitializationException(
ErrorState.NetworkWebviewRequest,
Exception("No connected events within the timeout!"),
ErrorState.InvalidHash,
Exception("Invalid webViewHash"),
params.config
)
}
} else {
webViewDataResult.getOrThrow().body.toString()
}
val webViewHash: String? = params.config.webViewHash
if (webViewHash != null && Utilities.Sha256(webViewData) != webViewHash) {
throw InitializationException(ErrorState.InvalidHash, Exception("Invalid webViewHash"), params.config)
}
if (webViewHash != null) {
Utilities.writeFile(File(SdkProperties.getLocalWebViewFile()), webViewData)
}
if (webViewHash != null) {
Utilities.writeFile(File(SdkProperties.getLocalWebViewFile()), webViewData)
LoadWebResult(params.config, webViewData)
}
LoadWebResult(params.config, webViewData)
}
data class Params(val config: Configuration) : BaseParams

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

@ -4,6 +4,7 @@ import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.connectivity.ConnectivityMonitor
import com.unity3d.services.core.connectivity.IConnectivityListener
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
@ -30,20 +31,22 @@ class InitializeStateNetworkError(
}
override suspend fun doWork(params: Params) = withContext(dispatchers.default) {
DeviceLog.error("Unity Ads init: network error, waiting for connection events")
maximumConnectedEvents = params.config.maximumConnectedEvents
connectedEventThreshold = params.config.connectedEventThreshold
runReturnSuspendCatching {
DeviceLog.error("Unity Ads init: network error, waiting for connection events")
maximumConnectedEvents = params.config.maximumConnectedEvents
connectedEventThreshold = params.config.connectedEventThreshold
val success = withTimeoutOrNull(params.config.networkErrorTimeout) {
suspendCancellableCoroutine<Unit> { cont ->
startListening(cont)
val success = withTimeoutOrNull(params.config.networkErrorTimeout) {
suspendCancellableCoroutine<Unit> { cont ->
startListening(cont)
}
}
}
// We timed out
if (success == null) {
ConnectivityMonitor.removeListener(this@InitializeStateNetworkError)
throw Exception("No connected events within the timeout!")
// We timed out
if (success == null) {
ConnectivityMonitor.removeListener(this@InitializeStateNetworkError)
throw Exception("No connected events within the timeout!")
}
}
}

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

@ -3,6 +3,7 @@ package com.unity3d.services.core.domain.task
import com.unity3d.services.core.api.Lifecycle
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import com.unity3d.services.core.log.DeviceLog
import com.unity3d.services.core.properties.ClientProperties
import com.unity3d.services.core.properties.SdkProperties
@ -26,37 +27,39 @@ open class InitializeStateReset(
return getMetricNameForInitializeTask("reset")
}
override suspend fun doWork(params: Params): Configuration =
override suspend fun doWork(params: Params): Result<Configuration> =
withContext(dispatchers.default) {
DeviceLog.debug("Unity Ads init: starting init")
runReturnSuspendCatching {
DeviceLog.debug("Unity Ads init: starting init")
val currentApp: WebViewApp? = WebViewApp.getCurrentApp()
val currentApp: WebViewApp? = WebViewApp.getCurrentApp()
currentApp?.resetWebViewAppInitialization()
if (currentApp?.webView != null) {
val success = withTimeoutOrNull(params.config.webViewAppCreateTimeout) {
withContext(dispatchers.main) {
currentApp.webView?.destroy()
currentApp.webView = null
currentApp?.resetWebViewAppInitialization()
if (currentApp?.webView != null) {
val success = withTimeoutOrNull(params.config.webViewAppCreateTimeout) {
withContext(dispatchers.main) {
currentApp.webView?.destroy()
currentApp.webView = null
}
}
}
if (success == null) {
throw Exception("Reset failed on opening ConditionVariable")
if (success == null) {
throw Exception("Reset failed on opening ConditionVariable")
}
}
unregisterLifecycleCallbacks()
SdkProperties.setCacheDirectory(null)
SdkProperties.getCacheDirectory() ?: throw Exception("Cache directory is NULL")
SdkProperties.setInitialized(false)
for (moduleName in params.config.moduleConfigurationList ?: emptyArray()) {
params.config.getModuleConfiguration(moduleName)?.resetState(params.config)
}
params.config
}
unregisterLifecycleCallbacks()
SdkProperties.setCacheDirectory(null)
SdkProperties.getCacheDirectory() ?: throw Exception("Cache directory is NULL")
SdkProperties.setInitialized(false)
for (moduleName in params.config.moduleConfigurationList ?: emptyArray()) {
params.config.getModuleConfiguration(moduleName)?.resetState(params.config)
}
params.config
}
private fun unregisterLifecycleCallbacks() {

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

@ -1,19 +0,0 @@
package com.unity3d.services.core.domain.task
import com.unity3d.services.core.configuration.Configuration
import com.unity3d.services.core.domain.ISDKDispatchers
import com.unity3d.services.core.extensions.runReturnSuspendCatching
import kotlinx.coroutines.withContext
class InitializeStateRetry(
private val dispatchers: ISDKDispatchers,
) : BaseTask<InitializeStateRetry.Params, Result<Unit>> {
override suspend fun doWork(params: Params): Result<Unit> = withContext(dispatchers.default) {
runReturnSuspendCatching {
// interactor logic
}
}
data class Params(val config: Configuration) : BaseParams
}

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

@ -17,8 +17,8 @@ abstract class MetricTask<in P : BaseParams, R> : BaseTask<P, R> {
var duration: Long = 0L
var taskStatus: String = "unknown"
override suspend fun invoke(params: P): R {
var result: R
override suspend fun invoke(params: P): Result<R> {
var result: Result<R>
duration = TimeUnit.NANOSECONDS.toMillis(
measureNanoTime {
result = super.invoke(params)
@ -27,12 +27,8 @@ abstract class MetricTask<in P : BaseParams, R> : BaseTask<P, R> {
return result
}
private fun captureMetric(result: R) {
taskStatus = if (result is Result<*>) {
if (result.isSuccess) "success" else "failure"
} else {
"success"
}
private fun captureMetric(result: Result<R>) {
taskStatus = if (result.isSuccess) "success" else "failure"
sendMetric()
}

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

@ -28,10 +28,6 @@ open class MetricSender(
override val metricEndPoint: String? = configuration.metricsUrl
override fun areMetricsEnabledForCurrentSession(): Boolean {
return true
}
override fun sendEvent(event: String, value: String?, tags: Map<String, String>) {
if (event.isEmpty()) {
DeviceLog.debug("Metric event not sent due to being null or empty: $event")

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