You've already forked Commercialization.topon
release: 1.4.8
This commit is contained in:
@@ -10,6 +10,9 @@ namespace AnyThinkAds
|
|||||||
{
|
{
|
||||||
public class ATAdsClientFactory
|
public class ATAdsClientFactory
|
||||||
{
|
{
|
||||||
|
private static IATInterstitialAdClient _sharedInterstitialAdClient;
|
||||||
|
private static IATRewardedVideoAdClient _sharedRewardedVideoAdClient;
|
||||||
|
|
||||||
public static IATBannerAdClient BuildBannerAdClient()
|
public static IATBannerAdClient BuildBannerAdClient()
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@@ -27,17 +30,25 @@ namespace AnyThinkAds
|
|||||||
|
|
||||||
public static IATInterstitialAdClient BuildInterstitialAdClient()
|
public static IATInterstitialAdClient BuildInterstitialAdClient()
|
||||||
{
|
{
|
||||||
|
if (_sharedInterstitialAdClient != null)
|
||||||
|
{
|
||||||
|
return _sharedInterstitialAdClient;
|
||||||
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
// Testing UNITY_EDITOR first because the editor also responds to the currently
|
// Testing UNITY_EDITOR first because the editor also responds to the currently
|
||||||
// selected platform.
|
// selected platform.
|
||||||
#elif UNITY_ANDROID
|
#elif UNITY_ANDROID
|
||||||
return new AnyThinkAds.Android.ATInterstitialAdClient();
|
_sharedInterstitialAdClient = new AnyThinkAds.Android.ATInterstitialAdClient();
|
||||||
|
return _sharedInterstitialAdClient;
|
||||||
#elif (UNITY_5 && UNITY_IOS) || UNITY_IPHONE
|
#elif (UNITY_5 && UNITY_IOS) || UNITY_IPHONE
|
||||||
return new AnyThinkAds.iOS.ATInterstitialAdClient();
|
_sharedInterstitialAdClient = new AnyThinkAds.iOS.ATInterstitialAdClient();
|
||||||
|
return _sharedInterstitialAdClient;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
return new UnityInterstitialClient();
|
_sharedInterstitialAdClient = new UnityInterstitialClient();
|
||||||
|
return _sharedInterstitialAdClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IATNativeAdClient BuildNativeAdClient()
|
public static IATNativeAdClient BuildNativeAdClient()
|
||||||
@@ -72,18 +83,26 @@ namespace AnyThinkAds
|
|||||||
|
|
||||||
public static IATRewardedVideoAdClient BuildRewardedVideoAdClient()
|
public static IATRewardedVideoAdClient BuildRewardedVideoAdClient()
|
||||||
{
|
{
|
||||||
|
if (_sharedRewardedVideoAdClient != null)
|
||||||
|
{
|
||||||
|
return _sharedRewardedVideoAdClient;
|
||||||
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
// Testing UNITY_EDITOR first because the editor also responds to the currently
|
// Testing UNITY_EDITOR first because the editor also responds to the currently
|
||||||
// selected platform.
|
// selected platform.
|
||||||
|
|
||||||
#elif UNITY_ANDROID
|
#elif UNITY_ANDROID
|
||||||
return new AnyThinkAds.Android.ATRewardedVideoAdClient();
|
_sharedRewardedVideoAdClient = new AnyThinkAds.Android.ATRewardedVideoAdClient();
|
||||||
|
return _sharedRewardedVideoAdClient;
|
||||||
#elif (UNITY_5 && UNITY_IOS) || UNITY_IPHONE
|
#elif (UNITY_5 && UNITY_IOS) || UNITY_IPHONE
|
||||||
return new AnyThinkAds.iOS.ATRewardedVideoAdClient();
|
_sharedRewardedVideoAdClient = new AnyThinkAds.iOS.ATRewardedVideoAdClient();
|
||||||
|
return _sharedRewardedVideoAdClient;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
return new UnityRewardedVideoAdClient();
|
_sharedRewardedVideoAdClient = new UnityRewardedVideoAdClient();
|
||||||
|
return _sharedRewardedVideoAdClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IATSDKAPIClient BuildSDKAPIClient()
|
public static IATSDKAPIClient BuildSDKAPIClient()
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
# [1.4.8]
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
|
||||||
|
* 统一奖励视频与插屏的 manual/auto 底层 client 回调源,修复自动加载模式下生命周期回调丢失问题。
|
||||||
|
* 修复奖励视频播放结束后 `mask` 未正常关闭的问题。
|
||||||
|
* 修复插屏成功关闭时误触发 `GLOBAL_ShowAwardVideoComplete` 的问题。
|
||||||
|
|
||||||
# [1.4.7]
|
# [1.4.7]
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
@@ -11,4 +19,3 @@
|
|||||||
* 激励和插屏切换为更稳的自动加载/预热策略,补强首次播放容错。
|
* 激励和插屏切换为更稳的自动加载/预热策略,补强首次播放容错。
|
||||||
* 修复插屏关闭、展示失败和播放失败回调链不完整的问题。
|
* 修复插屏关闭、展示失败和播放失败回调链不完整的问题。
|
||||||
* 调整 debug 行为:`topon.debug=true` 仅开启日志,不再自动弹出官方 DebugUI。
|
* 调整 debug 行为:`topon.debug=true` 仅开启日志,不再自动弹出官方 DebugUI。
|
||||||
|
|
||||||
|
|||||||
7
CHANGELOG.md.meta
Normal file
7
CHANGELOG.md.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6a68c48c5f3f3b747973e6acc508080e
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -40,6 +40,8 @@ public sealed class IAAAdDebugSampleGui : MonoBehaviour
|
|||||||
private GUIStyle _logStyle;
|
private GUIStyle _logStyle;
|
||||||
private bool _rewardedReadyCache;
|
private bool _rewardedReadyCache;
|
||||||
private bool _interstitialReadyCache;
|
private bool _interstitialReadyCache;
|
||||||
|
private bool _maskOpen;
|
||||||
|
private string _lastEventLog = "<none>";
|
||||||
private string _rewardedStatusCache = "Not refreshed yet.";
|
private string _rewardedStatusCache = "Not refreshed yet.";
|
||||||
private string _interstitialStatusCache = "Not refreshed yet.";
|
private string _interstitialStatusCache = "Not refreshed yet.";
|
||||||
private float _nextStatusRefreshTime;
|
private float _nextStatusRefreshTime;
|
||||||
@@ -168,6 +170,8 @@ public sealed class IAAAdDebugSampleGui : MonoBehaviour
|
|||||||
RefreshStatusCaches();
|
RefreshStatusCaches();
|
||||||
GUILayout.Label($"Rewarded Ready: {_rewardedReadyCache}", _textStyle);
|
GUILayout.Label($"Rewarded Ready: {_rewardedReadyCache}", _textStyle);
|
||||||
GUILayout.Label($"Interstitial Ready: {_interstitialReadyCache}", _textStyle);
|
GUILayout.Label($"Interstitial Ready: {_interstitialReadyCache}", _textStyle);
|
||||||
|
GUILayout.Label($"Mask Open: {_maskOpen}", _textStyle);
|
||||||
|
GUILayout.Label($"Last EventLog: {_lastEventLog}", _textStyle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -376,7 +380,17 @@ public sealed class IAAAdDebugSampleGui : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
_initInvoked = true;
|
_initInvoked = true;
|
||||||
var controller = new ToponAdController();
|
var controller = new SampleToponAdController(
|
||||||
|
onMaskChanged: isOpen =>
|
||||||
|
{
|
||||||
|
_maskOpen = isOpen;
|
||||||
|
AppendLog($"Mask changed => open={isOpen}");
|
||||||
|
},
|
||||||
|
onEventLog: (eventTable, eventValue, eventMessage) =>
|
||||||
|
{
|
||||||
|
_lastEventLog = $"{eventTable}:{eventValue}:{eventMessage}";
|
||||||
|
AppendLog($"EventLog => table={eventTable}, value={eventValue}, message={eventMessage}");
|
||||||
|
});
|
||||||
ADManager.Instance.Init(() =>
|
ADManager.Instance.Init(() =>
|
||||||
{
|
{
|
||||||
_initCallbackReceived = true;
|
_initCallbackReceived = true;
|
||||||
@@ -612,4 +626,39 @@ public sealed class IAAAdDebugSampleGui : MonoBehaviour
|
|||||||
richText = false
|
richText = false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class SampleToponAdController : IAdController
|
||||||
|
{
|
||||||
|
private readonly ToponAdController _inner = new ToponAdController();
|
||||||
|
private readonly Action<bool> _onMaskChanged;
|
||||||
|
private readonly Action<string, string, string> _onEventLog;
|
||||||
|
|
||||||
|
public SampleToponAdController(Action<bool> onMaskChanged, Action<string, string, string> onEventLog)
|
||||||
|
{
|
||||||
|
_onMaskChanged = onMaskChanged;
|
||||||
|
_onEventLog = onEventLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(ADConfig adConfig, object[] args)
|
||||||
|
{
|
||||||
|
_inner.Init(adConfig, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ADPlayer CreateAdPlayer(AD_Type type)
|
||||||
|
{
|
||||||
|
return _inner.CreateAdPlayer(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EventLog(string eventTable, string eventValue, string eventMessage = null)
|
||||||
|
{
|
||||||
|
_onEventLog?.Invoke(eventTable, eventValue, eventMessage);
|
||||||
|
_inner.EventLog(eventTable, eventValue, eventMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMask(bool isOpen)
|
||||||
|
{
|
||||||
|
_onMaskChanged?.Invoke(isOpen);
|
||||||
|
_inner.SetMask(isOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ public class ADListenerAggregator
|
|||||||
client.onAdCloseEvent += this.onInterstitialAdClose;
|
client.onAdCloseEvent += this.onInterstitialAdClose;
|
||||||
client.onAdShowEvent += this.onInterstitialAdShow;
|
client.onAdShowEvent += this.onInterstitialAdShow;
|
||||||
client.onAdShowFailureEvent += this.onInterstitialAdFailedToShow;
|
client.onAdShowFailureEvent += this.onInterstitialAdFailedToShow;
|
||||||
|
client.onAdVideoStartEvent += this.onInterstitialAdStartPlayingVideo;
|
||||||
|
client.onAdVideoEndEvent += this.onInterstitialAdEndPlayingVideo;
|
||||||
|
client.onAdVideoFailureEvent += this.onInterstitialAdFailedToPlayVideo;
|
||||||
|
client.onAdSourceAttemptEvent += this.startLoadingInterstitialAdSource;
|
||||||
|
client.onAdSourceFilledEvent += this.finishLoadingInterstitialAdSource;
|
||||||
|
client.onAdSourceLoadFailureEvent += this.failToLoadInterstitialAdSource;
|
||||||
|
client.onAdSourceBiddingAttemptEvent += this.startBiddingInterstitialAdSource;
|
||||||
|
client.onAdSourceBiddingFilledEvent += this.finishBiddingInterstitialAdSource;
|
||||||
|
client.onAdSourceBiddingFailureEvent += this.failBiddingInterstitialAdSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._interstitialListener = listener;
|
this._interstitialListener = listener;
|
||||||
@@ -222,4 +231,60 @@ public class ADListenerAggregator
|
|||||||
ToponUnityThread.Post(() => this._interstitialListener?.onInterstitialAdClick(atAdEventArgs.placementId,
|
ToponUnityThread.Post(() => this._interstitialListener?.onInterstitialAdClick(atAdEventArgs.placementId,
|
||||||
atAdEventArgs.callbackInfo));
|
atAdEventArgs.callbackInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onInterstitialAdStartPlayingVideo(object sender, ATAdEventArgs atAdEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.onInterstitialAdStartPlayingVideo(
|
||||||
|
atAdEventArgs.placementId, atAdEventArgs.callbackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onInterstitialAdEndPlayingVideo(object sender, ATAdEventArgs atAdEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.onInterstitialAdEndPlayingVideo(
|
||||||
|
atAdEventArgs.placementId, atAdEventArgs.callbackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onInterstitialAdFailedToPlayVideo(object sender, ATAdErrorEventArgs atAdErrorEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.onInterstitialAdFailedToPlayVideo(
|
||||||
|
atAdErrorEventArgs.placementId, atAdErrorEventArgs.errorCode, atAdErrorEventArgs.errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void startLoadingInterstitialAdSource(object sender, ATAdEventArgs atAdEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.startLoadingADSource(
|
||||||
|
atAdEventArgs.placementId, atAdEventArgs.callbackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void finishLoadingInterstitialAdSource(object sender, ATAdEventArgs atAdEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.finishLoadingADSource(
|
||||||
|
atAdEventArgs.placementId, atAdEventArgs.callbackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void failToLoadInterstitialAdSource(object sender, ATAdErrorEventArgs atAdErrorEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.failToLoadADSource(
|
||||||
|
atAdErrorEventArgs.placementId, atAdErrorEventArgs.callbackInfo, atAdErrorEventArgs.errorCode,
|
||||||
|
atAdErrorEventArgs.errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void startBiddingInterstitialAdSource(object sender, ATAdEventArgs atAdEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.startBiddingADSource(
|
||||||
|
atAdEventArgs.placementId, atAdEventArgs.callbackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void finishBiddingInterstitialAdSource(object sender, ATAdEventArgs atAdEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.finishBiddingADSource(
|
||||||
|
atAdEventArgs.placementId, atAdEventArgs.callbackInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void failBiddingInterstitialAdSource(object sender, ATAdErrorEventArgs atAdErrorEventArgs)
|
||||||
|
{
|
||||||
|
ToponUnityThread.Post(() => this._interstitialListener?.failBiddingADSource(
|
||||||
|
atAdErrorEventArgs.placementId, atAdErrorEventArgs.callbackInfo, atAdErrorEventArgs.errorCode,
|
||||||
|
atAdErrorEventArgs.errorMessage));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +1,76 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using AnyThinkAds.Api;
|
using AnyThinkAds.Api;
|
||||||
|
using AnyThinkAds.Common;
|
||||||
|
using AnyThinkAds.ThirdParty.LitJson;
|
||||||
using Runtime.ADAggregator;
|
using Runtime.ADAggregator;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener
|
public class AwardVideoPlayer : ADPlayer, ATRewardedVideoListener
|
||||||
{
|
{
|
||||||
private ATRewardedVideo _atRewardedVideo;
|
private IATRewardedVideoAdClient _client;
|
||||||
private ATRewardedAutoVideo _atRewardedAutoVideo;
|
private Action<bool> _onVideoComplete;
|
||||||
private Action<bool> _onVideoComplete;
|
|
||||||
private ADListenerAggregator _aggregator;
|
private ADListenerAggregator _aggregator;
|
||||||
private bool _autoLoadRegistered;
|
private bool _autoLoadRegistered;
|
||||||
|
|
||||||
public override void OnInit()
|
public override void OnInit()
|
||||||
{
|
{
|
||||||
this._atRewardedVideo = ATRewardedVideo.Instance;
|
_client = AnyThinkAds.ATAdsClientFactory.BuildRewardedVideoAdClient();
|
||||||
this._atRewardedAutoVideo = ATRewardedAutoVideo.Instance;
|
_aggregator = new ADListenerAggregator();
|
||||||
// this._atRewardedVideo.client.setListener(this); //由于新版本广告sdk弃用该方式,通过以下方式重新桥接监听事件
|
_aggregator.BindAwardVideoListener(_client, this);
|
||||||
this._aggregator = new ADListenerAggregator();
|
|
||||||
this._aggregator.BindAwardVideoListener(this._atRewardedVideo.client , this);
|
|
||||||
// var adClient = this._atRewardedVideo.client;
|
|
||||||
// adClient.on
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void ShowAD(Action onClose, Action<bool> onVideoComplete)
|
public override void ShowAD(Action onClose, Action<bool> onVideoComplete)
|
||||||
{
|
{
|
||||||
if (this.IsReadly())
|
if (!IsReadly())
|
||||||
{
|
{
|
||||||
this.curState = 0;
|
return;
|
||||||
this._onVideoComplete = onVideoComplete;
|
|
||||||
this.adListener.onClose = onClose;
|
|
||||||
this.adListener.onVideoComplete = this.OnVideoComplete;
|
|
||||||
var json = new Dictionary<string, string> { { AnyThinkAds.Api.ATConst.SCENARIO, this.AdScene } };
|
|
||||||
if (UseAutoLoad())
|
|
||||||
{
|
|
||||||
EnsureAutoLoadRegistered();
|
|
||||||
this._atRewardedAutoVideo.showAutoAd(this.Key, json);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this._atRewardedVideo.showAd(this.Key , json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curState = 0;
|
||||||
|
_onVideoComplete = onVideoComplete;
|
||||||
|
adListener.onClose = onClose;
|
||||||
|
adListener.onVideoComplete = OnVideoComplete;
|
||||||
|
var json = new Dictionary<string, string> { { ATConst.SCENARIO, AdScene } };
|
||||||
|
var mapJson = JsonMapper.ToJson(json);
|
||||||
|
|
||||||
|
if (UseAutoLoad())
|
||||||
|
{
|
||||||
|
EnsureAutoLoadRegistered();
|
||||||
|
_client.showAutoAd(Key, mapJson);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_client.showAd(Key, mapJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnVideoComplete(bool obj)
|
private void OnVideoComplete(bool obj)
|
||||||
{
|
{
|
||||||
this._onVideoComplete?.Invoke(obj);
|
_onVideoComplete?.Invoke(obj);
|
||||||
this._onVideoComplete = null;
|
_onVideoComplete = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsReadly()
|
public override bool IsReadly()
|
||||||
{
|
{
|
||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
if (this._atRewardedAutoVideo != null &&
|
if (_client != null && _client.autoLoadRewardedVideoReadyForPlacementID(Key))
|
||||||
this._atRewardedAutoVideo.autoLoadRewardedVideoReadyForPlacementID(this.Key))
|
|
||||||
{
|
{
|
||||||
this.curState = 2;
|
curState = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.curState == 2;
|
return curState == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.curState == 2)
|
if (curState == 2)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._atRewardedVideo != null && this._atRewardedVideo.hasAdReady(this.Key))
|
if (_client != null && _client.hasAdReady(Key))
|
||||||
{
|
{
|
||||||
this.curState = 2;
|
curState = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,74 +79,75 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener
|
|||||||
|
|
||||||
public override void LoadAD()
|
public override void LoadAD()
|
||||||
{
|
{
|
||||||
if (curState == 0)
|
if (curState != 0)
|
||||||
{
|
{
|
||||||
if (UseAutoLoad())
|
return;
|
||||||
{
|
|
||||||
EnsureAutoLoadRegistered();
|
|
||||||
this._atRewardedAutoVideo.setAutoLocalExtra(this.Key, BuildRewardedExtra());
|
|
||||||
this.curState = this._atRewardedAutoVideo.autoLoadRewardedVideoReadyForPlacementID(this.Key) ? 2 : 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._atRewardedVideo != null && this._atRewardedVideo.hasAdReady(this.Key))
|
|
||||||
{
|
|
||||||
this.curState = 2;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Dictionary<string,string> jsonmap = new Dictionary<string,string>();
|
|
||||||
//ATConst.USERID_KEY必传,用于标识每个用户;ATConst.USER_EXTRA_DATA为可选参数,传入后将透传到开发者的服务器
|
|
||||||
jsonmap = BuildRewardedExtra();
|
|
||||||
curState = 1;
|
|
||||||
this._atRewardedVideo.loadVideoAd(this.Key, jsonmap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#region SDK
|
if (UseAutoLoad())
|
||||||
|
{
|
||||||
|
EnsureAutoLoadRegistered();
|
||||||
|
_client.setAutoLocalExtra(Key, JsonMapper.ToJson(BuildRewardedExtra()));
|
||||||
|
curState = _client.autoLoadRewardedVideoReadyForPlacementID(Key) ? 2 : 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_client != null && _client.hasAdReady(Key))
|
||||||
|
{
|
||||||
|
curState = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
curState = 1;
|
||||||
|
_client.loadVideoAd(Key, JsonMapper.ToJson(BuildRewardedExtra()));
|
||||||
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdLoaded(string placementId)
|
public void onRewardedVideoAdLoaded(string placementId)
|
||||||
{
|
{
|
||||||
this.curState = 2;
|
Debug.Log($"[Topon] Rewarded loaded. placementId={placementId}");
|
||||||
|
curState = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdLoadFail(string placementId, string code, string message)
|
public void onRewardedVideoAdLoadFail(string placementId, string code, string message)
|
||||||
{
|
{
|
||||||
Debug.LogError($"激励视频加载失败: {message} , code:{code} , placementId: {placementId}");
|
Debug.LogError($"激励视频加载失败: {message} , code:{code} , placementId: {placementId}");
|
||||||
this.curState = 0;
|
curState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdPlayStart(string placementId, ATCallbackInfo callbackInfo)
|
public void onRewardedVideoAdPlayStart(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Rewarded play start. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdPlayEnd(string placementId, ATCallbackInfo callbackInfo)
|
public void onRewardedVideoAdPlayEnd(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Rewarded play end. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdPlayFail(string placementId, string code, string message)
|
public void onRewardedVideoAdPlayFail(string placementId, string code, string message)
|
||||||
{
|
{
|
||||||
Debug.LogError($"激励视频播放失败: {message} , code:{code} , placementId: {placementId}");
|
Debug.LogError($"激励视频播放失败: {message} , code:{code} , placementId: {placementId}");
|
||||||
curState = 0;
|
curState = 0;
|
||||||
this.adListener.OnShowError();
|
adListener.OnShowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdPlayClosed(string placementId, bool isReward, ATCallbackInfo callbackInfo)
|
public void onRewardedVideoAdPlayClosed(string placementId, bool isReward, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
this.adListener.OnRewardVerify(isReward , 1 , "");
|
Debug.Log($"[Topon] Rewarded closed. placementId={placementId}, reward={isReward}");
|
||||||
this.adListener.OnAdClose();
|
adListener.OnRewardVerify(isReward, 1, "");
|
||||||
|
adListener.OnAdClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRewardedVideoAdPlayClicked(string placementId, ATCallbackInfo callbackInfo)
|
public void onRewardedVideoAdPlayClicked(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Rewarded clicked. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReward(string placementId, ATCallbackInfo callbackInfo)
|
public void onReward(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
this.adListener.OnRewardVerify(true , 1 , "");
|
Debug.Log($"[Topon] Rewarded reward callback. placementId={placementId}");
|
||||||
this.adListener.OnAdClose();
|
adListener.OnRewardVerify(true, 1, "");
|
||||||
|
adListener.OnAdClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startLoadingADSource(string placementId, ATCallbackInfo callbackInfo)
|
public void startLoadingADSource(string placementId, ATCallbackInfo callbackInfo)
|
||||||
@@ -161,7 +160,7 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener
|
|||||||
|
|
||||||
public void failToLoadADSource(string placementId, ATCallbackInfo callbackInfo, string code, string message)
|
public void failToLoadADSource(string placementId, ATCallbackInfo callbackInfo, string code, string message)
|
||||||
{
|
{
|
||||||
this.OnError(code , message);
|
OnError(code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startBiddingADSource(string placementId, ATCallbackInfo callbackInfo)
|
public void startBiddingADSource(string placementId, ATCallbackInfo callbackInfo)
|
||||||
@@ -176,14 +175,12 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public override void OnPlayRequestStarted()
|
public override void OnPlayRequestStarted()
|
||||||
{
|
{
|
||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
EnsureAutoLoadRegistered();
|
EnsureAutoLoadRegistered();
|
||||||
this._atRewardedAutoVideo?.setAutoLocalExtra(this.Key, BuildRewardedExtra());
|
_client?.setAutoLocalExtra(Key, JsonMapper.ToJson(BuildRewardedExtra()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +203,7 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._atRewardedAutoVideo?.addAutoLoadAdPlacementID(new[] { this.Key });
|
_client?.addAutoLoadAdPlacementID(new[] { Key });
|
||||||
_autoLoadRegistered = true;
|
_autoLoadRegistered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,11 +226,10 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener
|
|||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
EnsureAutoLoadRegistered();
|
EnsureAutoLoadRegistered();
|
||||||
this._atRewardedAutoVideo?.entryAutoAdScenarioWithPlacementID(this.Key, scenario);
|
_client?.entryAutoAdScenarioWithPlacementID(Key, scenario);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._atRewardedVideo?.entryScenarioWithPlacementID(this.Key, scenario);
|
_client?.entryScenarioWithPlacementID(Key, scenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,147 +1,153 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using AnyThinkAds.Api;
|
using AnyThinkAds.Api;
|
||||||
|
using AnyThinkAds.Common;
|
||||||
|
using AnyThinkAds.ThirdParty.LitJson;
|
||||||
using Runtime.ADAggregator;
|
using Runtime.ADAggregator;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class InteractionPlayer : ADPlayer , ATInterstitialAdListener
|
public class InteractionPlayer : ADPlayer, ATInterstitialAdListener
|
||||||
{
|
{
|
||||||
private ATInterstitialAd _atInterstitialAd;
|
private IATInterstitialAdClient _client;
|
||||||
private ATInterstitialAutoAd _atInterstitialAutoAd;
|
|
||||||
private ADListenerAggregator _aggregator;
|
private ADListenerAggregator _aggregator;
|
||||||
private bool _autoLoadRegistered;
|
private bool _autoLoadRegistered;
|
||||||
|
|
||||||
public override void OnInit()
|
public override void OnInit()
|
||||||
{
|
{
|
||||||
this._atInterstitialAd = ATInterstitialAd.Instance;
|
_client = AnyThinkAds.ATAdsClientFactory.BuildInterstitialAdClient();
|
||||||
this._atInterstitialAutoAd = ATInterstitialAutoAd.Instance;
|
_aggregator = new ADListenerAggregator();
|
||||||
this._aggregator = new ADListenerAggregator();
|
_aggregator.BindInterstitialAdListener(_client, this);
|
||||||
this._aggregator.BindInterstitialAdListener(this._atInterstitialAd.client,this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ShowAD(Action onClose, Action<bool> onVideoComplete)
|
public override void ShowAD(Action onClose, Action<bool> onVideoComplete)
|
||||||
{
|
{
|
||||||
if (this.IsReadly())
|
if (!IsReadly())
|
||||||
{
|
{
|
||||||
this.adListener.onClose = onClose;
|
return;
|
||||||
this.adListener.onVideoComplete = onVideoComplete;
|
|
||||||
if (UseAutoLoad())
|
|
||||||
{
|
|
||||||
EnsureAutoLoadRegistered();
|
|
||||||
this._atInterstitialAutoAd.showAutoAd(this.Key, BuildInterstitialExtra());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ATInterstitialAd.Instance.showInterstitialAd(this.Key);
|
|
||||||
}
|
|
||||||
curState = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adListener.onClose = onClose;
|
||||||
|
adListener.onVideoComplete = onVideoComplete;
|
||||||
|
var mapJson = JsonMapper.ToJson(BuildInterstitialExtra());
|
||||||
|
|
||||||
|
if (UseAutoLoad())
|
||||||
|
{
|
||||||
|
EnsureAutoLoadRegistered();
|
||||||
|
_client.showAutoAd(Key, mapJson);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_client.showInterstitialAd(Key, mapJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
curState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsReadly()
|
public override bool IsReadly()
|
||||||
{
|
{
|
||||||
if (this.curState == 2)
|
if (curState == 2)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
if (this._atInterstitialAutoAd != null &&
|
if (_client != null && _client.autoLoadInterstitialAdReadyForPlacementID(Key))
|
||||||
this._atInterstitialAutoAd.autoLoadInterstitialAdReadyForPlacementID(this.Key))
|
|
||||||
{
|
{
|
||||||
this.curState = 2;
|
curState = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._atInterstitialAd != null && this._atInterstitialAd.hasInterstitialAdReady(this.Key))
|
if (_client != null && _client.hasInterstitialAdReady(Key))
|
||||||
{
|
{
|
||||||
this.curState = 2;
|
curState = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void LoadAD()
|
public override void LoadAD()
|
||||||
{
|
{
|
||||||
if (curState == 0)
|
if (curState != 0)
|
||||||
{
|
{
|
||||||
if (UseAutoLoad())
|
return;
|
||||||
{
|
|
||||||
EnsureAutoLoadRegistered();
|
|
||||||
this._atInterstitialAutoAd.setAutoLocalExtra(this.Key, BuildInterstitialExtra());
|
|
||||||
this.curState = this._atInterstitialAutoAd.autoLoadInterstitialAdReadyForPlacementID(this.Key) ? 2 : 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._atInterstitialAd != null && this._atInterstitialAd.hasInterstitialAdReady(this.Key))
|
|
||||||
{
|
|
||||||
this.curState = 2;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
//加载广告
|
|
||||||
Dictionary<string, object> jsonmap = new Dictionary<string, object>();
|
|
||||||
//只针对Sigmob,Sigmob的激励视频广告源当做插屏使用
|
|
||||||
// jsonmap.Add(AnyThinkAds.Api.ATConst.USE_REWARDED_VIDEO_AS_INTERSTITIAL, AnyThinkAds.Api.ATConst.USE_REWARDED_VIDEO_AS_INTERSTITIAL_NO);
|
|
||||||
var width = (int) (Screen.width * 0.7f);
|
|
||||||
ATSize atSize = new ATSize(width, (int) (width * 1.5f));
|
|
||||||
jsonmap.Add(ATInterstitialAdLoadingExtra.kATInterstitialAdLoadingExtraInterstitialAdSizeStruct , atSize);
|
|
||||||
curState = 1;
|
|
||||||
this._atInterstitialAd.loadInterstitialAd(this.Key, jsonmap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#region SDK
|
if (UseAutoLoad())
|
||||||
|
{
|
||||||
|
EnsureAutoLoadRegistered();
|
||||||
|
_client.setAutoLocalExtra(Key, JsonMapper.ToJson(BuildInterstitialExtra()));
|
||||||
|
curState = _client.autoLoadInterstitialAdReadyForPlacementID(Key) ? 2 : 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_client != null && _client.hasInterstitialAdReady(Key))
|
||||||
|
{
|
||||||
|
curState = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonmap = new Dictionary<string, object>();
|
||||||
|
var width = (int)(Screen.width * 0.7f);
|
||||||
|
var atSize = new ATSize(width, (int)(width * 1.5f));
|
||||||
|
jsonmap.Add(ATInterstitialAdLoadingExtra.kATInterstitialAdLoadingExtraInterstitialAdSizeStruct, atSize);
|
||||||
|
curState = 1;
|
||||||
|
_client.loadInterstitialAd(Key, JsonMapper.ToJson(jsonmap));
|
||||||
|
}
|
||||||
|
|
||||||
public void onInterstitialAdLoad(string placementId)
|
public void onInterstitialAdLoad(string placementId)
|
||||||
{
|
{
|
||||||
this.curState = 2;
|
Debug.Log($"[Topon] Interstitial loaded. placementId={placementId}");
|
||||||
|
curState = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdLoadFail(string placementId, string code, string message)
|
public void onInterstitialAdLoadFail(string placementId, string code, string message)
|
||||||
{
|
{
|
||||||
this.curState = 0;
|
Debug.LogError($"插屏加载失败: {message} , code:{code} , placementId: {placementId}");
|
||||||
|
curState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdShow(string placementId, ATCallbackInfo callbackInfo)
|
public void onInterstitialAdShow(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Interstitial show. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdFailedToShow(string placementId)
|
public void onInterstitialAdFailedToShow(string placementId)
|
||||||
{
|
{
|
||||||
this.curState = 0;
|
curState = 0;
|
||||||
this.adListener.OnShowError();
|
adListener.OnShowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdClose(string placementId, ATCallbackInfo callbackInfo)
|
public void onInterstitialAdClose(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
this.adListener.OnAdClose();
|
Debug.Log($"[Topon] Interstitial close. placementId={placementId}");
|
||||||
this.adListener.OnShowComplete();
|
adListener.OnAdClose();
|
||||||
|
adListener.OnShowComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdClick(string placementId, ATCallbackInfo callbackInfo)
|
public void onInterstitialAdClick(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Interstitial clicked. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdStartPlayingVideo(string placementId, ATCallbackInfo callbackInfo)
|
public void onInterstitialAdStartPlayingVideo(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Interstitial video start. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdEndPlayingVideo(string placementId, ATCallbackInfo callbackInfo)
|
public void onInterstitialAdEndPlayingVideo(string placementId, ATCallbackInfo callbackInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Log($"[Topon] Interstitial video end. placementId={placementId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInterstitialAdFailedToPlayVideo(string placementId, string code, string message)
|
public void onInterstitialAdFailedToPlayVideo(string placementId, string code, string message)
|
||||||
{
|
{
|
||||||
this.curState = 0;
|
curState = 0;
|
||||||
this.adListener.OnShowError();
|
adListener.OnShowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startLoadingADSource(string placementId, ATCallbackInfo callbackInfo)
|
public void startLoadingADSource(string placementId, ATCallbackInfo callbackInfo)
|
||||||
@@ -154,7 +160,7 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener
|
|||||||
|
|
||||||
public void failToLoadADSource(string placementId, ATCallbackInfo callbackInfo, string code, string message)
|
public void failToLoadADSource(string placementId, ATCallbackInfo callbackInfo, string code, string message)
|
||||||
{
|
{
|
||||||
this.OnError(code , message);
|
OnError(code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startBiddingADSource(string placementId, ATCallbackInfo callbackInfo)
|
public void startBiddingADSource(string placementId, ATCallbackInfo callbackInfo)
|
||||||
@@ -167,17 +173,15 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener
|
|||||||
|
|
||||||
public void failBiddingADSource(string placementId, ATCallbackInfo callbackInfo, string code, string message)
|
public void failBiddingADSource(string placementId, ATCallbackInfo callbackInfo, string code, string message)
|
||||||
{
|
{
|
||||||
this.OnError(code , message);
|
OnError(code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public override void OnPlayRequestStarted()
|
public override void OnPlayRequestStarted()
|
||||||
{
|
{
|
||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
EnsureAutoLoadRegistered();
|
EnsureAutoLoadRegistered();
|
||||||
this._atInterstitialAutoAd?.setAutoLocalExtra(this.Key, BuildInterstitialExtra());
|
_client?.setAutoLocalExtra(Key, JsonMapper.ToJson(BuildInterstitialExtra()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +204,7 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._atInterstitialAutoAd?.addAutoLoadAdPlacementID(new[] { this.Key });
|
_client?.addAutoLoadAdPlacementID(new[] { Key });
|
||||||
_autoLoadRegistered = true;
|
_autoLoadRegistered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +212,7 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener
|
|||||||
{
|
{
|
||||||
return new Dictionary<string, string>
|
return new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ ATConst.SCENARIO, this.AdScene ?? string.Empty }
|
{ ATConst.SCENARIO, AdScene ?? string.Empty }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,10 +226,10 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener
|
|||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
EnsureAutoLoadRegistered();
|
EnsureAutoLoadRegistered();
|
||||||
this._atInterstitialAutoAd?.entryAutoAdScenarioWithPlacementID(this.Key, scenario);
|
_client?.entryAutoAdScenarioWithPlacementID(Key, scenario);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._atInterstitialAd?.entryScenarioWithPlacementID(this.Key, scenario);
|
_client?.entryScenarioWithPlacementID(Key, scenario);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "com.commercialization.topon",
|
"name": "com.commercialization.topon",
|
||||||
"displayName": "Commercialization.topon",
|
"displayName": "Commercialization.topon",
|
||||||
"description": "基于topon的广告sdk封装,依赖基础商业化模块",
|
"description": "基于topon的广告sdk封装,依赖基础商业化模块",
|
||||||
"version": "1.4.7",
|
"version": "1.4.8",
|
||||||
"unity": "2021.1",
|
"unity": "2021.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies":
|
"dependencies":
|
||||||
{
|
{
|
||||||
"com.foldcc.cc-framework.commercialization" : "http://private.lightyears.ltd:18640/foldcc/CC-Framework.Commercialization.git#1.0.12"
|
"com.foldcc.cc-framework.commercialization" : "http://private.lightyears.ltd:18640/foldcc/CC-Framework.Commercialization.git#1.0.13"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Framework"
|
"Framework"
|
||||||
|
|||||||
Reference in New Issue
Block a user