This commit is contained in:
Developer
2026-06-18 10:06:49 +08:00
parent fdd81aef19
commit ebd5dafa2d
922 changed files with 145347 additions and 297 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d7b48bb0f4b64656a6b2bdba97e0058
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b623eff04d7c84163a2ab4387aa8206e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,177 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
using TapSDK.Core.Internal.Json;
using TapSDK.Core.Internal.Log;
namespace TapSDK.Core.Internal.Http {
public class TapHttpClient {
private readonly string clientId;
private readonly string clientToken;
private readonly string serverUrl;
readonly HttpClient client;
private Dictionary<string, Func<Task<string>>> runtimeHeaderTasks = new Dictionary<string, Func<Task<string>>>();
private Dictionary<string, string> additionalHeaders = new Dictionary<string, string>();
public TapHttpClient(string clientID, string clientToken, string serverUrl) {
this.clientId = clientID;
this.clientToken = clientToken;
this.serverUrl = serverUrl;
client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-LC-Id", clientID);
}
public void AddRuntimeHeaderTask(string key, Func<Task<string>> task) {
if (string.IsNullOrEmpty(key)) {
return;
}
if (task == null) {
return;
}
runtimeHeaderTasks[key] = task;
}
public void AddAddtionalHeader(string key, string value) {
if (string.IsNullOrEmpty(key)) {
return;
}
if (string.IsNullOrEmpty(value)) {
return;
}
additionalHeaders[key] = value;
}
public Task<T> Get<T>(string path,
Dictionary<string, object> headers = null,
Dictionary<string, object> queryParams = null,
bool withAPIVersion = true) {
return Request<T>(path, HttpMethod.Get, headers, null, queryParams, withAPIVersion);
}
public Task<T> Post<T>(string path,
Dictionary<string, object> headers = null,
object data = null,
Dictionary<string, object> queryParams = null,
bool withAPIVersion = true) {
return Request<T>(path, HttpMethod.Post, headers, data, queryParams, withAPIVersion);
}
public Task<T> Put<T>(string path,
Dictionary<string, object> headers = null,
object data = null,
Dictionary<string, object> queryParams = null,
bool withAPIVersion = true) {
return Request<T>(path, HttpMethod.Put, headers, data, queryParams, withAPIVersion);
}
public Task Delete(string path,
Dictionary<string, object> headers = null,
object data = null,
Dictionary<string, object> queryParams = null,
bool withAPIVersion = true) {
return Request<Dictionary<string, object>>(path, HttpMethod.Delete, headers, data, queryParams, withAPIVersion);
}
async Task<T> Request<T>(string path,
HttpMethod method,
Dictionary<string, object> headers = null,
object data = null,
Dictionary<string, object> queryParams = null,
bool withAPIVersion = true) {
string url = BuildUrl(path, queryParams);
HttpRequestMessage request = new HttpRequestMessage {
RequestUri = new Uri(url),
Method = method,
};
await FillHeaders(request.Headers, headers);
string content = null;
if (data != null) {
content = JsonConvert.SerializeObject(data);
StringContent requestContent = new StringContent(content);
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content = requestContent;
}
TapHttpUtils.PrintRequest(client, request, content);
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
request.Dispose();
string resultString = await response.Content.ReadAsStringAsync();
response.Dispose();
TapHttpUtils.PrintResponse(response, resultString);
if (response.IsSuccessStatusCode) {
T ret = JsonConvert.DeserializeObject<T>(resultString,
TapJsonConverter.Default);
return ret;
}
throw HandleErrorResponse(response.StatusCode, resultString);
}
TapException HandleErrorResponse(HttpStatusCode statusCode, string responseContent) {
int code = (int)statusCode;
string message = responseContent;
try {
// 尝试获取 LeanCloud 返回错误信息
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent,
TapJsonConverter.Default);
code = (int)error["code"];
message = error["error"].ToString();
} catch (Exception e) {
TapLog.Error(e.Message ?? "");
}
return new TapException(code, message);
}
string BuildUrl(string path, Dictionary<string, object> queryParams) {
string apiServer = serverUrl;
StringBuilder urlSB = new StringBuilder(apiServer.TrimEnd('/'));
urlSB.Append($"/{path}");
string url = urlSB.ToString();
if (queryParams != null) {
IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
string queries = string.Join("&", queryPairs);
url = $"{url}?{queries}";
}
return url;
}
async Task FillHeaders(HttpRequestHeaders headers, Dictionary<string, object> reqHeaders = null) {
// 额外 headers
if (reqHeaders != null) {
foreach (KeyValuePair<string, object> kv in reqHeaders) {
headers.Add(kv.Key, kv.Value.ToString());
}
}
if (additionalHeaders.Count > 0) {
foreach (KeyValuePair<string, string> kv in additionalHeaders) {
headers.Add(kv.Key, kv.Value);
}
}
// 服务额外 headers
foreach (KeyValuePair<string, Func<Task<string>>> kv in runtimeHeaderTasks) {
if (headers.Contains(kv.Key)) {
continue;
}
string value = await kv.Value.Invoke();
if (value == null) {
continue;
}
headers.Add(kv.Key, value);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7886abbfa16f547209a283a98e0b2895
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Net.Http;
using UnityEngine;
using TapSDK.Core.Internal.Log;
namespace TapSDK.Core.Internal.Http {
public class TapHttpUtils {
public static void PrintRequest(HttpClient client, HttpRequestMessage request, string content = null) {
if (TapLogger.LogDelegate == null) {
return;
}
if (client == null) {
return;
}
if (request == null) {
return;
}
StringBuilder sb = new StringBuilder();
sb.AppendLine("=== HTTP Request Start ===");
sb.AppendLine($"URL: {request.RequestUri}");
sb.AppendLine($"Method: {request.Method}");
sb.AppendLine($"Headers: ");
foreach (var header in client.DefaultRequestHeaders) {
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
}
foreach (var header in request.Headers) {
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
}
if (request.Content != null) {
foreach (var header in request.Content.Headers) {
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
}
}
if (!string.IsNullOrEmpty(content)) {
sb.AppendLine($"Content: {content}");
}
sb.AppendLine("=== HTTP Request End ===");
TapLog.Log(sb.ToString());
}
public static void PrintResponse(HttpResponseMessage response, string content = null) {
if (TapLogger.LogDelegate == null) {
return;
}
StringBuilder sb = new StringBuilder();
sb.AppendLine("=== HTTP Response Start ===");
sb.AppendLine($"URL: {response.RequestMessage.RequestUri}");
sb.AppendLine($"Status Code: {response.StatusCode}");
if (!string.IsNullOrEmpty(content)) {
sb.AppendLine($"Content: {content}");
}
sb.AppendLine("=== HTTP Response End ===");
TapLog.Log(sb.ToString());
}
public static NameValueCollection ParseQueryString(string queryString)
{
if (string.IsNullOrEmpty(queryString)) {
return null;
}
if (queryString.Length > 0 && queryString[0] == '?') {
queryString = queryString.Substring(1);
}
NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
string[] parts = segment.Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
queryParameters.Add(key, val);
}
}
return queryParameters;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c16aefb23bd7494d83c246505380cab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2d8e48cb41106433ba4f262639977f77
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
namespace TapSDK.Core.Internal.Init {
public interface IInitTask {
/// <summary>
/// 初始化顺序
/// </summary>
int Order { get; }
/// <summary>
/// 初始化
/// </summary>
/// <param name="config"></param>
void Init(TapTapSdkOptions coreOption, TapTapSdkBaseOptions[] otherOptions);
void Init(TapTapSdkOptions coreOption);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d28a8b38a6fd48249e82ff24e528080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 91e47674c73b246fdbda4fb286209bd3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace TapSDK.Core.Internal.Json {
public class TapJsonConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return objectType == typeof(object);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
if (reader.TokenType == JsonToken.StartObject) {
var obj = new Dictionary<string, object>();
serializer.Populate(reader, obj);
return obj;
}
if (reader.TokenType == JsonToken.StartArray) {
var arr = new List<object>();
serializer.Populate(reader, arr);
return arr;
}
if (reader.TokenType == JsonToken.Integer) {
if ((long)reader.Value < int.MaxValue) {
return Convert.ToInt32(reader.Value);
}
}
if (reader.TokenType == JsonToken.Float) {
return Convert.ToSingle(reader.Value);
}
return serializer.Deserialize(reader);
}
public readonly static TapJsonConverter Default = new TapJsonConverter();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e7910a9ac70c45509b1520d9d614ce0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ee253dc23c74a430e91bd9791fb9043d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,150 @@
using System;
using UnityEngine;
namespace TapSDK.Core.Internal.Log
{
public class TapLog
{
private const string TAG = "TapSDK";
// 颜色常量
private const string InfoColor = "#FFFFFF"; // 白色
private const string WarningColor = "#FFFF00"; // 黄色
private const string ErrorColor = "#FF0000"; // 红色
private const string MainThreadColor = "#00FF00"; // 绿色
private const string IOThreadColor = "#FF00FF"; // 紫色
private const string TagColor = "#00FFFF"; // 青色
// 开关变量,控制是否启用日志输出
public static bool Enabled = false;
private string module;
private string tag;
public TapLog(string module, string tag = TAG)
{
this.tag = tag;
this.module = module;
}
public void Log(string message, string detail = null)
{
TapLog.Log(message, detail, tag, module);
}
// 输出带有自定义颜色和标签的警告
public void Warning(string message, string detail = null)
{
TapLog.Warning(message, detail, tag, module);
}
// 输出带有自定义颜色和标签的错误
public void Error(string message, string detail = null)
{
TapLog.Error(message, detail, tag, module);
}
public static void Error(Exception e)
{
TapLog.Error(e?.Message ?? "");
}
// 输出带有自定义颜色和标签的普通日志
public static void Log(string message, string detail = null, string tag = TAG, string module = null)
{
if (string.IsNullOrEmpty(message))
{
return;
}
string msg = GetFormattedMessage(message: message, detail: detail, colorHex: InfoColor, tag: tag, module: module);
if (TapLogger.LogDelegate != null)
{
TapLogger.Debug(msg);
return;
}
if (Enabled)
{
Debug.Log(msg);
}
}
// 输出带有自定义颜色和标签的警告
public static void Warning(string message, string detail = null, string tag = TAG, string module = null)
{
if (string.IsNullOrEmpty(message))
{
return;
}
string msg = GetFormattedMessage(message: message, detail: detail, colorHex: WarningColor, tag: tag, module: module);
if (TapLogger.LogDelegate != null)
{
TapLogger.Warn(msg);
return;
}
if (Enabled)
{
Debug.LogWarning(msg);
}
}
// 输出带有自定义颜色和标签的错误
public static void Error(string message, string detail = null, string tag = TAG, string module = null)
{
if (string.IsNullOrEmpty(message))
{
return;
}
string msg = GetFormattedMessage(message: message, detail: detail, colorHex: ErrorColor, tag: tag, module: module);
if (TapLogger.LogDelegate != null)
{
TapLogger.Error(msg);
return;
}
Debug.LogError(msg);
}
// 格式化带有颜色和标签的消息
private static string GetFormattedMessage(string message, string detail, string colorHex, string tag, string module)
{
string threadInfo = GetThreadInfo();
string tagColor = TagColor;
if (module != null && module != "")
{
tag = $"{tag}.{module}";
}
if (IsMobilePlatform())
{
return $"[{tag}] {threadInfo} {message}\n{detail}";
}
else
{
return $"<color={tagColor}>[{tag}]</color> {threadInfo} <color={colorHex}>{message}</color>\n{detail}\n";
}
}
// 获取当前线程信息
private static string GetThreadInfo()
{
bool isMainThread = System.Threading.Thread.CurrentThread.IsAlive && System.Threading.Thread.CurrentThread.ManagedThreadId == 1;
string threadInfo = isMainThread ? "Main" : $"IO {System.Threading.Thread.CurrentThread.ManagedThreadId}";
if (IsMobilePlatform())
{
// 移动平台的线程信息不使用颜色
return $"({threadInfo})";
}
else
{
// 其他平台的线程信息使用颜色
string color = isMainThread ? MainThreadColor : IOThreadColor;
return $"<color={color}>({threadInfo})</color>";
}
}
// 检查是否是移动平台
private static bool IsMobilePlatform()
{
return Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9613fc51bb6646d89b77a0fde4d4b40
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 93f336f19f5594d78bf1cd3f6ca3e876
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TapSDK.Core.Internal
{
public interface ITapCorePlatform
{
void Init(TapTapSdkOptions config);
void Init(TapTapSdkOptions coreOption, TapTapSdkBaseOptions[] otherOptions);
void UpdateLanguage(TapTapLanguageType language);
Task<bool> IsLaunchedFromTapTapPC();
void SendOpenLog(
string project,
string version,
string action,
Dictionary<string, string> properties
);
#if UNITY_STANDALONE_WIN
void RegisterTapTapPCStateChangeListener(Action<int> action);
void UnRegisterTapTapPCStateChangeListener(Action<int> action);
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 693f034729dd341468d896eecf035ed2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
using System;
namespace TapSDK.Core.Internal {
public interface ITapEventPlatform {
void Init(TapTapEventOptions eventOptions);
void SetUserID(string userID);
void SetUserID(string userID, string properties);
void ClearUser();
string GetDeviceId();
void LogEvent(string name, string properties);
void DeviceInitialize(string properties);
void DeviceUpdate(string properties);
void DeviceAdd(string properties);
void UserInitialize(string properties);
void UserUpdate(string properties);
void UserAdd(string properties);
void AddCommonProperty(string key, string value);
void AddCommon(string properties);
void ClearCommonProperty(string key);
void ClearCommonProperties(string[] keys);
void ClearAllCommonProperties();
void LogChargeEvent(string orderID, string productName, long amount, string currencyType, string paymentMethod, string properties);
void RegisterDynamicProperties(Func<string> callback);
void SetOAID(string value);
void LogDeviceLoginEvent();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b1164922a79c4217bc3f9dc0cac0ff3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System;
using System.Linq;
using TapSDK.Core.Internal.Log;
using UnityEngine;
namespace TapSDK.Core.Internal {
public static class PlatformTypeUtils {
/// <summary>
/// 创建平台接口实现类对象
/// </summary>
/// <param name="interfaceType"></param>
/// <param name="startWith"></param>
/// <returns></returns>
public static object CreatePlatformImplementationObject(Type interfaceType, string startWith) {
// 获取所有符合条件的程序集
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(assembly => assembly.GetName().FullName.StartsWith(startWith));
// 获取符合条件的类型
Type platformSupportType = assemblies
.SelectMany(assembly => assembly.GetTypes())
.SingleOrDefault(clazz => interfaceType.IsAssignableFrom(clazz) && clazz.IsClass);
if (platformSupportType != null) {
try
{
return Activator.CreateInstance(platformSupportType);
}
catch (Exception ex)
{
TapLog.Error($"Failed to create instance of {platformSupportType.FullName}: {ex}");
}
} else {
TapLog.Error($"No type found that implements {interfaceType} in assemblies starting with {startWith}");
}
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22c79a001ad8e4956a7af00198f39c33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd5e6d0a0a3084dbe9172f6644b9afee
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 02106d3e1676c4ae283349aad59eaf9d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using System;
namespace TapSDK.UI
{
public enum EOpenMode
{
Sync,
Async,
}
[Flags]
public enum EAnimationMode
{
None = 0,
Alpha = 1 << 0,
Scale = 1 << 1,
RightSlideIn = 1 << 2,
UpSlideIn = 1 << 3,
ToastSlideIn = 1 << 4,
AlphaAndScale = Alpha | Scale,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bd4da1dce99741fca662c311f199c2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace TapTap.UI
{
/// <summary>
/// 修复UnityUGui中Canvas画布在Windows平台宽高比过长横屏拉很长导致UI的最右边一部分无法被点击的Bug,现发现与2021.3.x版本
/// </summary>
public class GraphicRaycasterBugFixed : GraphicRaycaster
{
[NonSerialized] private List<Graphic> m_RaycastResults = new List<Graphic>();
private Canvas _Canvas;
private Canvas canvasBugFixed
{
get
{
if (_Canvas != null)
return _Canvas;
_Canvas = GetComponent<Canvas>();
return _Canvas;
}
}
/// <summary>
/// Perform the raycast against the list of graphics associated with the Canvas.
/// </summary>
/// <param name="eventData">Current event data</param>
/// <param name="resultAppendList">List of hit objects to append new results to.</param>
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
{
if (canvasBugFixed == null)
return;
var canvasGraphics = GraphicRegistry.GetGraphicsForCanvas(canvasBugFixed);
if (canvasGraphics == null || canvasGraphics.Count == 0)
return;
int displayIndex;
var currentEventCamera = eventCamera; // Property can call Camera.main, so cache the reference
if (canvasBugFixed.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
displayIndex = canvasBugFixed.targetDisplay;
else
displayIndex = currentEventCamera.targetDisplay;
//!! 不同之处
var eventPosition = Display.RelativeMouseAt(eventData.position);
//MultipleDisplayUtilities.RelativeMouseAtScaled(eventData.position);
if (eventPosition != Vector3.zero)
{
// We support multiple display and display identification based on event position.
int eventDisplayIndex = (int)eventPosition.z;
// Discard events that are not part of this display so the user does not interact with multiple displays at once.
if (eventDisplayIndex != displayIndex)
return;
}
else
{
// The multiple display system is not supported on all platforms, when it is not supported the returned position
// will be all zeros so when the returned index is 0 we will default to the event data to be safe.
eventPosition = eventData.position;
// We dont really know in which display the event occured. We will process the event assuming it occured in our display.
}
// Convert to view space
Vector2 pos;
if (currentEventCamera == null)
{
// Multiple display support only when not the main display. For display 0 the reported
// resolution is always the desktops resolution since its part of the display API,
// so we use the standard none multiple display method. (case 741751)
float w = Screen.width;
float h = Screen.height;
if (displayIndex > 0 && displayIndex < Display.displays.Length)
{
w = Display.displays[displayIndex].systemWidth;
h = Display.displays[displayIndex].systemHeight;
}
pos = new Vector2(eventPosition.x / w, eventPosition.y / h);
}
else
pos = currentEventCamera.ScreenToViewportPoint(eventPosition);
// If it's outside the camera's viewport, do nothing
if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)
return;
float hitDistance = float.MaxValue;
Ray ray = new Ray();
if (currentEventCamera != null)
ray = currentEventCamera.ScreenPointToRay(eventPosition);
if (canvasBugFixed.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
{
float distanceToClipPlane = 100.0f;
if (currentEventCamera != null)
{
float projectionDirection = ray.direction.z;
distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection)
? Mathf.Infinity
: Mathf.Abs((currentEventCamera.farClipPlane - currentEventCamera.nearClipPlane) / projectionDirection);
}
#if PACKAGE_PHYSICS
if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
{
if (ReflectionMethodsCache.Singleton.raycast3D != null)
{
var hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, (int)m_BlockingMask);
if (hits.Length > 0)
hitDistance = hits[0].distance;
}
}
#endif
#if PACKAGE_PHYSICS2D
if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
{
if (ReflectionMethodsCache.Singleton.raycast2D != null)
{
var hits = ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, (int)m_BlockingMask);
if (hits.Length > 0)
hitDistance = hits[0].distance;
}
}
#endif
}
m_RaycastResults.Clear();
Raycast(canvasBugFixed, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults);
int totalCount = m_RaycastResults.Count;
for (var index = 0; index < totalCount; index++)
{
var go = m_RaycastResults[index].gameObject;
bool appendGraphic = true;
if (ignoreReversedGraphics)
{
if (currentEventCamera == null)
{
// If we dont have a camera we know that we should always be facing forward
var dir = go.transform.rotation * Vector3.forward;
appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
}
else
{
// If we have a camera compare the direction against the cameras forward.
var cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
// !!不同之处
appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
}
}
if (appendGraphic)
{
float distance = 0;
Transform trans = go.transform;
Vector3 transForward = trans.forward;
if (currentEventCamera == null || canvasBugFixed.renderMode == RenderMode.ScreenSpaceOverlay)
distance = 0;
else
{
// http://geomalgorithms.com/a06-_intersect-2.html
distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));
// Check to see if the go is behind the camera.
if (distance < 0)
continue;
}
if (distance >= hitDistance)
continue;
var castResult = new RaycastResult
{
gameObject = go,
module = this,
distance = distance,
screenPosition = eventPosition,
displayIndex = displayIndex,
index = resultAppendList.Count,
depth = m_RaycastResults[index].depth,
sortingLayer = canvasBugFixed.sortingLayerID,
sortingOrder = canvasBugFixed.sortingOrder,
worldPosition = ray.origin + ray.direction * distance,
worldNormal = -transForward
};
resultAppendList.Add(castResult);
}
}
}
/// <summary>
/// Perform a raycast into the screen and collect all graphics underneath it.
/// </summary>
[NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
{
// Necessary for the event system
int totalCount = foundGraphics.Count;
for (int i = 0; i < totalCount; ++i)
{
Graphic graphic = foundGraphics[i];
// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
continue;
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera))
continue;
if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
continue;
if (graphic.Raycast(pointerPosition, eventCamera))
{
s_SortedGraphics.Add(graphic);
}
}
s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
totalCount = s_SortedGraphics.Count;
for (int i = 0; i < totalCount; ++i)
results.Add(s_SortedGraphics[i]);
s_SortedGraphics.Clear();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 39341cb1058fe4bdebd02bc59f5c08e1
timeCreated: 1678941027

View File

@@ -0,0 +1,29 @@
using UnityEngine;
namespace TapSDK.UI
{
public class LoadingPanelController : BasePanelController
{
public Transform rotater;
public float speed = 10;
/// <summary>
/// bind ugui components for every panel
/// </summary>
protected override void BindComponents()
{
rotater = transform.Find("Image");;
}
private void Update()
{
if (rotater != null)
{
var localEuler = rotater.localEulerAngles;
var z = rotater.localEulerAngles.z;
z += Time.deltaTime * speed;
rotater.localEulerAngles = new Vector3(localEuler.x, localEuler.y, z);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 270dfb0d584134275afb9640c5abde2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,102 @@
using System;
using UnityEngine;
namespace TapSDK.UI
{
public class MonoSingleton<T> : MonoBehaviour where T : Component
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null && Application.isPlaying)
{
CreateInstance();
}
return _instance;
}
}
private static void CreateInstance()
{
Type theType = typeof(T);
_instance = (T)FindObjectOfType(theType);
if (_instance == null)
{
var go = new GameObject(typeof(T).Name);
_instance = go.AddComponent<T>();
GameObject rootObj = GameObject.Find("TapSDKSingletons");
if (rootObj == null)
{
rootObj = new GameObject("TapSDKSingletons");
DontDestroyOnLoad(rootObj);
}
go.transform.SetParent(rootObj.transform);
}
}
public static void DestroyInstance()
{
if (_instance != null)
{
Destroy(_instance.gameObject);
}
}
public static bool HasInstance()
{
return _instance != null;
}
protected virtual void Awake()
{
if (_instance != null && _instance.gameObject != gameObject)
{
if (Application.isPlaying)
{
Destroy(gameObject);
}
else
{
DestroyImmediate(gameObject); // UNITY_EDITOR
}
return;
}
else if (_instance == null)
{
_instance = GetComponent<T>();
}
DontDestroyOnLoad(gameObject);
Init();
}
protected virtual void OnDestroy()
{
Uninit();
if (_instance != null && _instance.gameObject == gameObject)
{
_instance = null;
}
}
public virtual void Init()
{
}
public virtual void Uninit()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2743c376f0b344d32a58d36d253b50ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using System;
namespace TapSDK.UI
{
public class Singleton<T> where T : class
{
private static T _instance;
private static readonly object _lock = new object();
public static T Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = (T)Activator.CreateInstance(typeof(T), true);
}
}
}
return _instance;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 641507a330f89486c951299818e8d14e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace TapSDK.UI
{
public class TipPanelOpenParam : AbstractOpenPanelParameter
{
public string text;
public Color textColor;
public TextAnchor textAnchor;
public TipPanelOpenParam(string text, Color textColor, TextAnchor textAnchor)
{
this.text = text;
this.textColor = textColor;
this.textAnchor = textAnchor;
}
}
public class TipPanelController : BasePanelController
{
public Text text;
protected override void BindComponents()
{
base.BindComponents();
text = transform.Find("Text").GetComponent<Text>();
}
protected override void OnLoadSuccess()
{
base.OnLoadSuccess();
TipPanelOpenParam param = this.openParam as TipPanelOpenParam;
if (param != null)
{
text.text = param.text;
text.color = param.textColor;
text.alignment = param.textAnchor;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4bf8716943aca4601b13dfac46ccf44b
timeCreated: 1690867588

View File

@@ -0,0 +1,95 @@
using UnityEngine;
using UnityEngine.UI;
namespace TapSDK.UI {
public class ToastPanelOpenParam : AbstractOpenPanelParameter {
public float popupTime;
public string text;
public Texture icon;
public ToastPanelOpenParam(string text, float popupTime, Texture icon = null) {
this.text = text;
this.popupTime = popupTime;
this.icon = icon;
}
}
public class ToastBlackPanelController : BasePanelController {
private const int MAX_CHAR_COUNT = 87;
private const float MIN_HORIZONTAL_LENGTH = 210;
private const float MAX_HORIZONTAL_LENGTH = 1252;
[HideInInspector]
public Text text;
[HideInInspector]
public RectTransform background;
[HideInInspector]
public LayoutGroup layout;
[HideInInspector]
public RawImage iconImage;
public float fixVal;
public float animationTime;
[SerializeField]
private float sizeDeltaX = 50f;
protected override void BindComponents() {
base.BindComponents();
background = transform.Find("Root/BGM") as RectTransform;
layout = background.transform.Find("Container").GetComponent<LayoutGroup>();
text = layout.transform.Find("Text").GetComponent<Text>();
iconImage = layout.transform.Find("Mask/Icon").GetComponent<RawImage>();
fadeAnimationTime = animationTime;
}
protected override void OnLoadSuccess() {
base.OnLoadSuccess();
ToastPanelOpenParam param = this.openParam as ToastPanelOpenParam;
if (param != null) {
iconImage.gameObject.SetActive(param.icon != null);
layout.enabled = param.icon != null;
text.text = param.text;
bool overflow;
// test: 已登录账号:海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边边...
var totalLength = CalculateLengthOfText(out overflow);
if (overflow) {
bool haveIcon = param.icon != null;
int length = Mathf.Min(MAX_CHAR_COUNT - (haveIcon ? 4 : 0), param.text.Length - 1);
var transformedText = param.text.Substring(0, length) + "...";
text.text = transformedText;
}
var x = totalLength;
var y = background.sizeDelta.y;
background.sizeDelta = new Vector2(x, y);
var textRect = text.transform as RectTransform;
if (param.icon != null) {
iconImage.texture = param.icon;
text.alignment = TextAnchor.MiddleLeft;
textRect.sizeDelta = new Vector2(x - sizeDeltaX, 24);
}
else {
text.alignment = TextAnchor.MiddleCenter;
textRect.anchoredPosition = Vector2.zero;
textRect.anchorMin = Vector2.zero;
textRect.anchorMax = Vector2.one;
textRect.sizeDelta = Vector2.zero;
}
this.Invoke("Close", param.popupTime);
}
}
protected float CalculateLengthOfText( out bool horizontalOverflow) {
var width = text.preferredWidth + fixVal + 12;
width = Mathf.Max(MIN_HORIZONTAL_LENGTH, width);
var maxWidth = MAX_HORIZONTAL_LENGTH;
horizontalOverflow = width > maxWidth;
width = Mathf.Min(maxWidth, width);
return width;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cfadb0c2f9cbd4e26821f6acbffb6dac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using UnityEngine;
using UnityEngine.UI;
namespace TapSDK.UI {
public class ToastWhitePanelController : BasePanelController {
private const int MAX_CHAR_COUNT = 69;
private const float MIN_HORIZONTAL_LENGTH = 120;
private const float MAX_HORIZONTAL_LENGTH = 1252;
private const float BACKGROUND_WIDTH_HELPER = 60;
[HideInInspector]
public Text text;
[HideInInspector]
public RectTransform background;
[HideInInspector]
public RawImage iconImage;
public float fixVal;
public float animationTime;
public ToastPanelOpenParam toastParam;
protected override void BindComponents() {
base.BindComponents();
background = transform.Find("Root/BGM") as RectTransform;
text = background.transform.Find("Container/Text").GetComponent<Text>();
iconImage = background.transform.Find("Container/Image").GetComponent<RawImage>();
fadeAnimationTime = animationTime;
}
protected override void OnLoadSuccess() {
base.OnLoadSuccess();
ToastPanelOpenParam param = this.openParam as ToastPanelOpenParam;
toastParam = param;
if (param != null) {
iconImage.gameObject.SetActive(param.icon != null);
text.text = param.text;
bool overflow = text.text.Length > MAX_CHAR_COUNT;
// test: 已登录账号:海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边晒太阳海绵宝宝和派大星一起在海边边...
var totalLength = CalculateLengthOfText();
if (overflow) {
bool haveIcon = param.icon != null;
int length = Mathf.Min(MAX_CHAR_COUNT - (haveIcon ? 4 : 0), param.text.Length - 1);
var transformedText = param.text.Substring(0, length) + "...";
text.text = transformedText;
}
var x = totalLength;
var y = background.sizeDelta.y;
background.sizeDelta = new Vector2(x + BACKGROUND_WIDTH_HELPER, y);
var textRect = text.transform as RectTransform;
if (param.icon != null) {
iconImage.texture = param.icon;
textRect.sizeDelta = new Vector2(x, y);
}
else {
textRect.anchoredPosition = Vector2.zero;
textRect.anchorMin = Vector2.zero;
textRect.anchorMax = Vector2.one;
textRect.sizeDelta = Vector2.zero;
}
this.Invoke("Close", param.popupTime);
}
}
private float CalculateLengthOfText() {
var width = text.preferredWidth + fixVal;
width = Mathf.Max(MIN_HORIZONTAL_LENGTH, width);
var maxWidth = MAX_HORIZONTAL_LENGTH;
width = Mathf.Min(maxWidth, width);
return width;
}
public void Refresh(float refreshTime) {
this.CancelInvoke("Close");
this.Invoke("Close", refreshTime);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e2bbf05d8861431bb645d167b1ab76c
timeCreated: 1695367197

View File

@@ -0,0 +1,725 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TapSDK.Core.Internal.Log;
using UnityEngine;
using UnityEngine.UI;
namespace TapSDK.UI
{
public class UIManager : MonoSingleton<UIManager>
{
public enum GeneralToastLevel {
None = 0,
Info = 1,
Warning = 2,
Error = 3,
Success =4,
}
private class ToastConfig {
public bool white;
public string text;
public float popupTime;
public Texture icon;
}
private static Texture whiteToastSuccessIcon;
private static Texture whiteToastErrorIcon;
private static Texture whiteToastInfoIcon;
private static Texture whiteToastWarningIcon;
private static Texture taptapToastIcon;
public static Texture WhiteToastSuccessIcon {
get {
if (whiteToastSuccessIcon == null)
whiteToastSuccessIcon = UnityEngine.Resources.Load<Texture>("taptap-toast-success");
return whiteToastSuccessIcon;
}
}
public static Texture WhiteToastErrorIcon {
get {
if (whiteToastErrorIcon == null)
whiteToastErrorIcon = UnityEngine.Resources.Load<Texture>("taptap-toast-error");
return whiteToastErrorIcon;
}
}
public static Texture WhiteToastWarningIcon {
get {
if (whiteToastWarningIcon == null)
whiteToastWarningIcon = UnityEngine.Resources.Load<Texture>("taptap-toast-warning");
return whiteToastWarningIcon;
}
}
public static Texture WhiteToastInfoIcon {
get {
if (whiteToastInfoIcon == null)
whiteToastInfoIcon = UnityEngine.Resources.Load<Texture>("taptap-toast-info");
return whiteToastInfoIcon;
}
}
public static Texture TapTapToastIcon {
get {
if (taptapToastIcon == null)
taptapToastIcon = Resources.Load<Texture>("TapSDKCommonTapIcon-v2");
return taptapToastIcon;
}
}
private List<ToastConfig> _toastCacheData = new List<ToastConfig>(12);
public const int TOPPEST_SORTING_ORDER = 32767;
private const int LOADING_PANEL_SORTING_ORDER = TOPPEST_SORTING_ORDER;
private const int TOAST_PANEL_SORTING_ORDER = TOPPEST_SORTING_ORDER - 10;
private const int TIP_PANEL_SORTING_ORDER = TOPPEST_SORTING_ORDER - 20;
private const int REFERENCE_WIDTH = 1920;
private const int REFERENCE_HEIGHT = 1080;
private readonly Dictionary<Type, BasePanelController> _registerPanels = new Dictionary<Type, BasePanelController>();
// uicamera
private Camera _uiCamera;
private GameObject _uiRoot;
private GameObject _uiConstantRoot;
private Canvas _uiRootCanvas;
public Camera UiCamera => _uiCamera;
public override void Init()
{
if (_uiRoot == null && Application.isPlaying)
{
CreateUIRoot();
}
}
private void CreateUIRoot()
{
_uiRoot = Instantiate(Resources.Load<GameObject>("TapSDKUIRoot"));
DontDestroyOnLoad(_uiRoot);
var canvas = _uiRoot.GetComponent<Canvas>();
_uiCamera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? Camera.main : canvas.worldCamera;
_uiRoot.transform.SetParent(UIManager.Instance.transform);
_uiRootCanvas = _uiRoot.transform.GetComponent<Canvas>();
CanvasScaler scaler = _uiRoot.GetComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
scaler.referenceResolution = new Vector2(REFERENCE_WIDTH, REFERENCE_HEIGHT);
float referenceRatio = REFERENCE_WIDTH * 1.0f / REFERENCE_HEIGHT;
float screenRatio = Screen.width * 1.0f / Screen.height;
scaler.matchWidthOrHeight = screenRatio > referenceRatio ? 1 : 0;
_uiConstantRoot = Instantiate(Resources.Load<GameObject>("TapSDKConstantUIRoot"));
DontDestroyOnLoad(_uiConstantRoot);
canvas = _uiConstantRoot.GetComponent<Canvas>();
_uiCamera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? Camera.main : canvas.worldCamera;
_uiConstantRoot.transform.SetParent(UIManager.Instance.transform);
_uiRootCanvas = _uiConstantRoot.transform.GetComponent<Canvas>();
}
public override void Uninit()
{
_uiCamera = null;
_uiRoot = null;
_uiConstantRoot = null;
_uiRootCanvas = null;
base.Uninit();
}
/// <summary>
/// Get Or Create UI asynchronously
/// </summary>
/// <param name="path">the prefab path</param>
/// <param name="param">opening param</param>
/// <param name="onAsyncGet">if u want to get ui do something,here is for u, which is invoked after BasePanelController.OnLoadSuccess</param>
/// <typeparam name="TPanelController"></typeparam>
/// <returns>get panel instance if sync mode load</returns>
public async Task<TPanelController> OpenUIAsync<TPanelController>(string path, AbstractOpenPanelParameter param = null, Action<BasePanelController> onAsyncGet = null) where TPanelController : BasePanelController
{
var basePanelController = GetUI<TPanelController>();
// 如果已有界面(之前缓存过的),则不执行任何操作
if (basePanelController != null)
{
if (!basePanelController.canvas.enabled)
{
basePanelController.canvas.enabled = true;
}
onAsyncGet?.Invoke(basePanelController);
return basePanelController;
}
ResourceRequest request = Resources.LoadAsync<GameObject>(path);
while (request.isDone == false)
{
await Task.Yield();
}
GameObject go = request.asset as GameObject;
var basePanel = Instantiate(go).GetComponent<TPanelController>();
if (basePanel != null)
{
var prefabRectTransform = basePanel.transform as RectTransform;
var anchorMin = prefabRectTransform.anchorMin;
var anchorMax = prefabRectTransform.anchorMax;
var anchoredPosition = prefabRectTransform.anchoredPosition;
var sizeDelta = prefabRectTransform.sizeDelta;
InternalOnPanelLoaded(typeof(TPanelController), basePanel, param);
if (IsNeedCorrectRectTransform(basePanel))
{
var panelRectTransform = basePanel.transform as RectTransform;
panelRectTransform.anchorMin = anchorMin;
panelRectTransform.anchorMax = anchorMax;
panelRectTransform.anchoredPosition = anchoredPosition;
panelRectTransform.sizeDelta = sizeDelta;
}
onAsyncGet?.Invoke(basePanel);
EnsureSpecialPanel();
return basePanel;
}
else
{
return null;
}
}
/// <summary>
/// Get Or Create UI asynchronously
/// </summary>
/// <param name="panelType">the panel Type</param>
/// <param name="path">the prefab path</param>
/// <param name="param">opening param</param>
/// <param name="onAsyncGet">if u want to get ui do something,here is for u, which is invoked after BasePanelController.OnLoadSuccess</param>
/// <returns>get panel instance if sync mode load</returns>
public async Task<BasePanelController> OpenUIAsync(Type panelType, string path, AbstractOpenPanelParameter param = null, Action<BasePanelController> onAsyncGet = null)
{
if (!typeof(BasePanelController).IsAssignableFrom(panelType))
{
return null;
}
var basePanelController = GetUI(panelType);
// 如果已有界面(之前缓存过的),则不执行任何操作
if (basePanelController != null)
{
if (!basePanelController.canvas.enabled)
{
basePanelController.canvas.enabled = true;
}
onAsyncGet?.Invoke(basePanelController);
return basePanelController;
}
ResourceRequest request = Resources.LoadAsync<GameObject>(path);
while (request.isDone == false)
{
await Task.Yield();
}
GameObject go = request.asset as GameObject;
var basePanel = Instantiate(go).GetComponent<BasePanelController>();
if (basePanel != null)
{
var prefabRectTransform = basePanel.transform as RectTransform;
var anchorMin = prefabRectTransform.anchorMin;
var anchorMax = prefabRectTransform.anchorMax;
var anchoredPosition = prefabRectTransform.anchoredPosition;
var sizeDelta = prefabRectTransform.sizeDelta;
InternalOnPanelLoaded(panelType, basePanel, param);
if (IsNeedCorrectRectTransform(basePanel))
{
var panelRectTransform = basePanel.transform as RectTransform;
panelRectTransform.anchorMin = anchorMin;
panelRectTransform.anchorMax = anchorMax;
panelRectTransform.anchoredPosition = anchoredPosition;
panelRectTransform.sizeDelta = sizeDelta;
}
onAsyncGet?.Invoke(basePanel);
EnsureSpecialPanel();
return basePanel;
}
else
{
return null;
}
}
/// <summary>
/// Get Or Create UI
/// </summary>
/// <param name="path">the prefab path</param>
/// <param name="param">opening param</param>
/// <typeparam name="TPanelController"></typeparam>
/// <returns>get panel instance if sync mode load</returns>
public TPanelController OpenUI<TPanelController>(string path, AbstractOpenPanelParameter param = null) where TPanelController : BasePanelController
{
TPanelController basePanelController = GetUI<TPanelController>();
// 如果已有界面(之前缓存过的),则不执行任何操作
if (basePanelController != null)
{
if (!basePanelController.canvas.enabled)
{
basePanelController.canvas.enabled = true;
}
return basePanelController;
}
var go = Resources.Load(path) as GameObject;
if (go != null)
{
var prefabRectTransform = go.transform as RectTransform;
var anchorMin = prefabRectTransform.anchorMin;
var anchorMax = prefabRectTransform.anchorMax;
var anchoredPosition = prefabRectTransform.anchoredPosition;
var sizeDelta = prefabRectTransform.sizeDelta;
GameObject panelGo = GameObject.Instantiate(go);
var basePanel = panelGo.GetComponent<TPanelController>();
if (basePanel == null) {
basePanel = panelGo.AddComponent<TPanelController>();
}
_registerPanels.Add(typeof(TPanelController), basePanel);
basePanel.transform.SetAsLastSibling();
if (IsNeedCorrectRectTransform(basePanel))
{
var panelRectTransform = basePanel.transform as RectTransform;
panelRectTransform.anchorMin = anchorMin;
panelRectTransform.anchorMax = anchorMax;
panelRectTransform.anchoredPosition = anchoredPosition;
panelRectTransform.sizeDelta = sizeDelta;
}
basePanel.OnLoaded(param);
EnsureSpecialPanel();
return basePanel;
}
return null;
}
/// <summary>
/// Get Or Create UI
/// </summary>
/// <param name="panelType">panel type MUST based on BasePanelController</param>
/// <param name="path">the prefab path</param>
/// <param name="param">opening param</param>
/// <returns>get panel instance if sync mode load</returns>
public BasePanelController OpenUI(Type panelType, string path, AbstractOpenPanelParameter param = null)
{
if (!typeof(BasePanelController).IsAssignableFrom(panelType))
{
return null;
}
var basePanelController = GetUI(panelType);
// 如果已有界面(之前缓存过的),则不执行任何操作
if (basePanelController != null)
{
if (basePanelController != null && !basePanelController.canvas.enabled)
{
basePanelController.canvas.enabled = true;
}
return basePanelController;
}
var panelGo = Resources.Load(path) as GameObject;
if (panelGo != null)
{
var prefabRectTransform = panelGo.transform as RectTransform;
var anchorMin = prefabRectTransform.anchorMin;
var anchorMax = prefabRectTransform.anchorMax;
var anchoredPosition = prefabRectTransform.anchoredPosition;
var sizeDelta = prefabRectTransform.sizeDelta;
var basePanel = GameObject.Instantiate(panelGo).GetComponent<BasePanelController>();
_registerPanels.Add(panelType, basePanel);
// basePanel.SetOpenOrder(uiOpenOrder);
basePanel.transform.SetAsLastSibling();
if (IsNeedCorrectRectTransform(basePanel))
{
var panelRectTransform = basePanel.transform as RectTransform;
panelRectTransform.anchorMin = anchorMin;
panelRectTransform.anchorMax = anchorMax;
panelRectTransform.anchoredPosition = anchoredPosition;
panelRectTransform.sizeDelta = sizeDelta;
}
basePanel.OnLoaded(param);
EnsureSpecialPanel();
return basePanel;
}
return null;
}
public BasePanelController GetUI(Type panelType)
{
if (!typeof(BasePanelController).IsAssignableFrom(panelType))
{
return null;
}
if (_registerPanels.TryGetValue(panelType, out BasePanelController basePanelController))
{
return basePanelController;
}
return null;
}
public TPanelController GetUI<TPanelController>() where TPanelController : BasePanelController
{
Type panelType = typeof(TPanelController);
if (_registerPanels.TryGetValue(panelType, out BasePanelController panel))
{
return (TPanelController)panel;
}
return null;
}
public bool CloseUI(Type panelType)
{
if (!typeof(BasePanelController).IsAssignableFrom(panelType))
{
return false;
}
BasePanelController baseController = GetUI(panelType);
if (baseController != null)
{
if (panelType == typeof(BasePanelController)) // 标尺界面是测试界面 不用关闭
return false;
else
baseController.Close();
return true;
}
return false;
}
public bool CloseUI<TPanelController>() where TPanelController : BasePanelController
{
TPanelController panel = GetUI<TPanelController>();
if (panel != null)
{
panel.Close();
return true;
}
return false;
}
/// <summary>
/// add ui would invoked after create ui automatically, don't need mannually call it in most case
/// </summary>
public void AddUI(BasePanelController panel)
{
if (panel == null)
{
return;
}
var rt = panel.transform as RectTransform;
Vector2 cacheAP = Vector2.zero;
Vector2 cacheOffsetMax = Vector2.zero;
Vector2 cacheOffsetMin = Vector2.zero;
Vector2 cacheSizeDelta = Vector2.zero;
if (rt != null)
{
cacheAP = rt.anchoredPosition;
cacheOffsetMax = rt.offsetMax;
cacheOffsetMin = rt.offsetMin;
cacheSizeDelta = rt.sizeDelta;
}
panel.transform.SetParent(panel.AttachedParent);
if (rt != null)
{
rt.anchoredPosition = cacheAP;
rt.offsetMax = cacheOffsetMax;
rt.offsetMin = cacheOffsetMin;
rt.sizeDelta = cacheSizeDelta;
}
panel.transform.localScale = Vector3.one;
panel.transform.localRotation = Quaternion.identity;
panel.gameObject.name = panel.gameObject.name.Replace("(Clone)", "");
}
/// <summary>
/// remove ui would invoked automatically, don't need mannually call it in most case
/// </summary>
public void RemoveUI(BasePanelController panel)
{
if (panel == null)
{
return;
}
Type panelType = panel.GetType();
if (_registerPanels.ContainsKey(panelType))
{
_registerPanels.Remove(panelType);
}
if (panelType == typeof(ToastBlackPanelController) || panelType == typeof(ToastWhitePanelController)) {
CheckToastCache();
}
panel.Dispose();
}
private void CheckToastCache() {
if (_toastCacheData.Count > 0) {
var config = _toastCacheData[0];
InternalOpenToast(config.white, config.text, config.popupTime, config.icon);
_toastCacheData.RemoveAt(0);
}
}
/// <summary>
/// take some ui to the most top layer
/// </summary>
/// <param name="panelType"></param>
public void ToppedUI(Type panelType)
{
if (!typeof(BasePanelController).IsAssignableFrom(panelType))
{
return;
}
ToppedUI(GetUI(panelType));
}
/// <summary>
/// take some ui to the most top layer
/// </summary>
/// <param name="panel"></param>
/// <param name="toppedSepcialUI">特殊 UI(Loading,Toast之类) 是否需要重新制定</param>
public void ToppedUI(BasePanelController panel, bool toppedSepcialUI = true)
{
if (panel == null)
{
return;
}
// panel.SetOpenOrder(uiOpenOrder);
panel.transform.SetAsLastSibling();
if (toppedSepcialUI) EnsureSpecialPanel();
}
/// <summary>
/// open toast panel for tip info
/// </summary>
public void OpenToast(bool white, string text, float popupTime = 3.0f, Texture icon = null) {
// print this function parameters
if (!string.IsNullOrEmpty(text) && popupTime > 0) {
bool isShowing = GetUI<ToastWhitePanelController>() != null || GetUI<ToastBlackPanelController>() != null;
if (ToastTryMerge(white, text, popupTime, icon)) {
return;
}
if (isShowing) {
var config = new ToastConfig() {
white = white,
text = text,
popupTime = popupTime,
icon = icon,
};
_toastCacheData.Add(config);
}
else {
InternalOpenToast(white, text, popupTime, icon);
}
}
}
private bool ToastTryMerge(bool white, string text, float popupTime, Texture icon) {
var currentToastPanel = GetUI<ToastWhitePanelController>();
if (currentToastPanel?.toastParam != null) {
var param = currentToastPanel?.toastParam;
// 目前只有白色 Toast 进行 Merge
bool sameToast = param.icon == icon && param.text == text && white;
if (sameToast) {
currentToastPanel.Refresh(popupTime);
return true;
}
}
return false;
}
/// <summary>
/// 打开通用 Toast(白色底)
/// </summary>
/// <param name="text"></param>
/// <param name="toastLevel">设置 Toast 提示级别,会展示提示图片,如果不想要提示图片,请使用 GeneralToastLevel.None</param>
/// <param name="popupTime"></param>
public void OpenToast(string text, GeneralToastLevel toastLevel, float popupTime = 3.0f) {
OpenToast(true, text, popupTime, GetToastIcon(toastLevel));
}
private Texture GetToastIcon(GeneralToastLevel toastLevel) {
if (toastLevel == GeneralToastLevel.None)
return null;
switch (toastLevel) {
case GeneralToastLevel.Info:
return UIManager.WhiteToastInfoIcon;
case GeneralToastLevel.Warning:
return UIManager.WhiteToastWarningIcon;
case GeneralToastLevel.Error:
return UIManager.WhiteToastErrorIcon;
case GeneralToastLevel.Success:
return UIManager.WhiteToastSuccessIcon;
}
return null;
}
private void InternalOpenToast(bool white, string text, float popupTime, Texture icon) {
if (string.IsNullOrEmpty(text) || popupTime <= 0) return;
if (white) {
var toast = OpenUI<ToastWhitePanelController>("TapCommonToastWhite", new ToastPanelOpenParam(text, popupTime, icon));
if (toast != null) {
toast.transform.SetAsLastSibling();
}
}
else {
var toast = OpenUI<ToastBlackPanelController>("TapCommonToastBlack", new ToastPanelOpenParam(text, popupTime, icon));
if (toast != null) {
toast.transform.SetAsLastSibling();
}
}
}
/// <summary>
/// open toast panel for tip info
/// </summary>
public void OpenTip(string text, Color textColor, TextAnchor textAnchor = TextAnchor.MiddleCenter)
{
if (!string.IsNullOrEmpty(text))
{
TapLog.Log("[UIManager] Call OpenTip text: " + text);
var tip = OpenUI<TipPanelController>("TapCommonTip", new TipPanelOpenParam(text, textColor, textAnchor));
if (tip != null)
{
tip.transform.SetAsLastSibling();
}
}
}
/// <summary>
/// open loading panel that would at the toppest layer and block interaction
/// </summary>
public void OpenLoading()
{
var loadingPanel = OpenUI<LoadingPanelController>("Loading");
if (loadingPanel != null)
{
// https://www.reddit.com/r/Unity3D/comments/2b1g1l/order_in_layer_maximum_value/
// loadingPanel.SetOpenOrder(LOADING_PANEL_SORTING_ORDER);
loadingPanel.transform.SetAsLastSibling();
}
}
/// <summary>
/// open loading panel that would at the toppest layer and block interaction
/// </summary>
public async Task OpenLoadingAsync()
{
var loadingPanel = await OpenUIAsync<LoadingPanelController>("Loading");
if (loadingPanel != null)
{
// https://www.reddit.com/r/Unity3D/comments/2b1g1l/order_in_layer_maximum_value/
// loadingPanel.SetOpenOrder(LOADING_PANEL_SORTING_ORDER);
loadingPanel.transform.SetAsLastSibling();
}
}
public void CloseLoading()
{
var loadingPanel = GetUI<LoadingPanelController>();
if (loadingPanel != null)
{
loadingPanel.Close();
}
}
public void CloseTip()
{
var panel = GetUI<TipPanelController>();
if (panel != null)
{
panel.Close();
}
}
private bool IsNeedCorrectRectTransform(BasePanelController controller)
{
if (controller == null) return false;
return (controller.panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.None;
}
private void InternalOnPanelLoaded(Type tPanelController, BasePanelController basePanel, AbstractOpenPanelParameter param = null)
{
_registerPanels.Add(tPanelController, basePanel);
basePanel.OnLoaded(param);
// basePanel.SetOpenOrder(uiOpenOrder);
basePanel.transform.SetAsLastSibling();
}
private void EnsureSpecialPanel()
{
var temp = _registerPanels.Where(panel =>
panel.Value != null && panel.Value.gameObject.activeInHierarchy && panel.Value.toppedOrder != 0);
var toppedPanels = new List<KeyValuePair<Type,BasePanelController>>(temp);
if (toppedPanels.Count == 0) return;
toppedPanels.Sort((x,y)=>x.Value.toppedOrder.CompareTo(y.Value.toppedOrder));
foreach (var toppedPanel in toppedPanels)
{
toppedPanel.Value.transform.SetAsLastSibling();
}
}
public Transform GetUIRootTransform(BasePanelController panel)
{
if (panel?.openParam != null && panel.openParam.NeedConstantResolution)
return _uiConstantRoot.transform;
else
return _uiRoot.transform;
}
#region external api
public void SetSortOrder(int order)
{
_uiRootCanvas.sortingOrder = order;
}
public Camera GetUICamera()
{
return _uiCamera;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 753f42bc9ae8a4035ae94f4733957527
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 930467091d41f45c09b2d67e0a3ced30
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,386 @@
using System;
using System.Collections;
using TapSDK.Core.Internal.Log;
using UnityEngine;
using UnityEngine.UI;
namespace TapSDK.UI
{
/// <summary>
/// base panel of TapSDK UI module
/// </summary>
[RequireComponent(typeof(CanvasGroup))]
[RequireComponent(typeof(GraphicRaycaster))]
public abstract class BasePanelController : MonoBehaviour
{
protected virtual float GetToastSlideInOffset() => 300;
/// <summary>
/// the canvas related to this panel
/// </summary>
[HideInInspector]
public Canvas canvas;
/// <summary>
/// the canvas group related to this panel
/// </summary>
[HideInInspector]
public CanvasGroup canvasGroup;
/// <summary>
/// fade in/out time
/// </summary>
protected float fadeAnimationTime = 0.15f;
/// <summary>
/// animation elapse time
/// </summary>
private float _animationElapse;
private Vector2 _screenSize;
private Vector2 _cachedAnchorPos;
private RectTransform _rectTransform;
private Coroutine _animationCoroutine;
/// <summary>
/// open parameter
/// </summary>
protected internal AbstractOpenPanelParameter openParam;
/// <summary>
/// settings about this panel
/// </summary>
public BasePanelConfig panelConfig;
/// <summary>
/// 特殊面板需要一直保持置顶的,需要填写 toppedOrder, toppedOrder 越大越置顶
/// </summary>
public int toppedOrder;
/// <summary>
/// the transform parent when created it would be attached to
/// </summary>
/// <value></value>
public virtual Transform AttachedParent => UIManager.Instance.GetUIRootTransform(this);
#region Load
protected virtual void Awake()
{
canvas = GetComponent<Canvas>();
canvasGroup = GetComponent<CanvasGroup>();
_rectTransform = transform as RectTransform;
_screenSize = new Vector2(Screen.width, Screen.height);
#if UNITY_EDITOR
if (canvas == null)
{
TapLog.Log("[TapSDK UI] BasePanel Must Be Related To Canvas Component!");
}
#endif
}
/// <summary>
/// bind ugui components for every panel
/// </summary>
protected virtual void BindComponents() {}
/// <summary>
/// create the prefab instance
/// </summary>
/// <param name="param"></param>
public void OnLoaded(AbstractOpenPanelParameter param = null)
{
openParam = param;
// 寻找组件
BindComponents();
// 添加到控制层
UIManager.Instance.AddUI(this);
// 更新层级信息
InitCanvasSetting();
// 开始动画效果
OnShowEffectStart();
// 调用加载成功方法
OnLoadSuccess();
}
private void InitCanvasSetting()
{
if (canvas.renderMode != RenderMode.ScreenSpaceOverlay)
{
var camera = UIManager.Instance.GetUICamera();
if (camera != null)
{
canvas.worldCamera = camera;
}
}
canvas.pixelPerfect = true;
// canvas.overrideSorting = true;
}
/// <summary>
/// init panel logic here
/// </summary>
protected virtual void OnLoadSuccess()
{
}
#endregion
#region Animation
protected virtual void OnShowEffectStart()
{
if (panelConfig.animationType == EAnimationMode.None)
{
return;
}
if ((panelConfig.animationType & EAnimationMode.Alpha) == EAnimationMode.Alpha)
{
canvasGroup.alpha = 0;
}
if ((panelConfig.animationType & EAnimationMode.Scale) == EAnimationMode.Scale)
{
transform.localScale = Vector3.zero;
}
if ((panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.RightSlideIn)
{
_cachedAnchorPos = _rectTransform.anchoredPosition;
_rectTransform.anchoredPosition += new Vector2(_screenSize.x, 0);
}
if ((panelConfig.animationType & EAnimationMode.UpSlideIn) == EAnimationMode.UpSlideIn) {
_cachedAnchorPos = _rectTransform.anchoredPosition;
_rectTransform.anchoredPosition += new Vector2(0, _screenSize.y);
}
if ((panelConfig.animationType & EAnimationMode.ToastSlideIn) == EAnimationMode.ToastSlideIn) {
_cachedAnchorPos = _rectTransform.anchoredPosition;
_rectTransform.anchoredPosition += new Vector2(0, GetToastSlideInOffset());
}
OnEffectStart();
_animationCoroutine = StartCoroutine(FadeInCoroutine(fadeAnimationTime));
}
protected virtual void OnShowEffectEnd()
{
OnEffectEnd();
}
protected virtual void OnCloseEffectStart()
{
OnEffectStart();
if ((panelConfig.animationType & EAnimationMode.Alpha) == EAnimationMode.Alpha)
{
canvasGroup.alpha = 1;
}
if ((panelConfig.animationType & EAnimationMode.Scale) == EAnimationMode.Scale)
{
transform.localScale = Vector3.one;
}
if ((panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.RightSlideIn)
{
_rectTransform.anchoredPosition = _cachedAnchorPos;
}
if ((panelConfig.animationType & EAnimationMode.UpSlideIn) == EAnimationMode.UpSlideIn)
{
_rectTransform.anchoredPosition = _cachedAnchorPos;
}
if ((panelConfig.animationType & EAnimationMode.ToastSlideIn) == EAnimationMode.ToastSlideIn)
{
_rectTransform.anchoredPosition = _cachedAnchorPos;
}
_animationCoroutine = StartCoroutine(FadeOutCoroutine(fadeAnimationTime));
}
protected virtual void OnCloseEffectEnd()
{
OnEffectEnd();
GameObject.Destroy(gameObject);
}
private void OnEffectStart()
{
_animationElapse = 0;
if (_animationCoroutine != null)
{
StopCoroutine(_animationCoroutine);
_animationCoroutine = null;
}
canvasGroup.interactable = false;
}
private void OnEffectEnd()
{
canvasGroup.interactable = true;
_animationElapse = 0;
_animationCoroutine = null;
}
private IEnumerator FadeInCoroutine(float time)
{
while (_animationElapse < time)
{
yield return null;
_animationElapse += Time.deltaTime;
float value = Mathf.Clamp01(_animationElapse / time);
if ((panelConfig.animationType & EAnimationMode.Alpha) == EAnimationMode.Alpha)
{
canvasGroup.alpha = value;
}
if ((panelConfig.animationType & EAnimationMode.Scale) == EAnimationMode.Scale)
{
transform.localScale = new Vector3(value, value, value);
}
if ((panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.RightSlideIn)
{
var temp = (1 - value) * _screenSize.x;
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x + temp, _cachedAnchorPos.y);
}
if ((panelConfig.animationType & EAnimationMode.UpSlideIn) == EAnimationMode.UpSlideIn)
{
var temp = (1 - value) * _screenSize.y;
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x, _cachedAnchorPos.y + + temp);
}
if ((panelConfig.animationType & EAnimationMode.ToastSlideIn) == EAnimationMode.ToastSlideIn)
{
var temp = (1 - value) * GetToastSlideInOffset();
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x, _cachedAnchorPos.y + + temp);
}
}
if ((panelConfig.animationType & EAnimationMode.Alpha) == EAnimationMode.Alpha)
{
canvasGroup.alpha = 1;
}
if ((panelConfig.animationType & EAnimationMode.Scale) == EAnimationMode.Scale)
{
transform.localScale = Vector3.one;
}
if ((panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.RightSlideIn)
{
_rectTransform.anchoredPosition = _cachedAnchorPos;
}
if ((panelConfig.animationType & EAnimationMode.UpSlideIn) == EAnimationMode.UpSlideIn)
{
_rectTransform.anchoredPosition = _cachedAnchorPos;
}
if ((panelConfig.animationType & EAnimationMode.ToastSlideIn) == EAnimationMode.ToastSlideIn)
{
_rectTransform.anchoredPosition = _cachedAnchorPos;
}
OnShowEffectEnd();
}
private IEnumerator FadeOutCoroutine(float time)
{
while (_animationElapse < time)
{
yield return null;
_animationElapse += Time.deltaTime;
float value = 1 - Mathf.Clamp01(_animationElapse / time);
if ((panelConfig.animationType & EAnimationMode.Alpha) == EAnimationMode.Alpha)
{
canvasGroup.alpha = value;
}
if ((panelConfig.animationType & EAnimationMode.Scale) == EAnimationMode.Scale)
{
transform.localScale = new Vector3(value, value, value);
}
if ((panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.RightSlideIn)
{
var temp = (1 - value) * _screenSize.x;
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x + temp, _cachedAnchorPos.y);
}
if ((panelConfig.animationType & EAnimationMode.UpSlideIn) == EAnimationMode.UpSlideIn)
{
var temp = (1 - value) * _screenSize.y;
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x, _cachedAnchorPos.y + temp);
}
if ((panelConfig.animationType & EAnimationMode.ToastSlideIn) == EAnimationMode.ToastSlideIn)
{
var temp = (1 - value) * GetToastSlideInOffset();
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x, _cachedAnchorPos.y + temp);
}
}
if ((panelConfig.animationType & EAnimationMode.Alpha) == EAnimationMode.Alpha)
{
canvasGroup.alpha = 0;
}
if ((panelConfig.animationType & EAnimationMode.Scale) == EAnimationMode.Scale)
{
transform.localScale = Vector3.zero;
}
if ((panelConfig.animationType & EAnimationMode.RightSlideIn) == EAnimationMode.RightSlideIn)
{
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x + _screenSize.x, _cachedAnchorPos.y);
}
if ((panelConfig.animationType & EAnimationMode.UpSlideIn) == EAnimationMode.UpSlideIn)
{
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x, _cachedAnchorPos.y + _screenSize.y);
}
if ((panelConfig.animationType & EAnimationMode.ToastSlideIn) == EAnimationMode.ToastSlideIn)
{
_rectTransform.anchoredPosition = new Vector2(_cachedAnchorPos.x, _cachedAnchorPos.y + GetToastSlideInOffset());
}
OnCloseEffectEnd();
}
#endregion
/// <summary>
/// on receive resolution change event
/// </summary>
/// <param name="res"></param>
protected virtual void UIAdapt(Vector2Int res) {}
/// <summary>
/// common close api
/// </summary>
public virtual void Close()
{
UIManager.Instance.RemoveUI(this);
}
/// <summary>
/// set canvas sorting order
/// </summary>
/// <param name="openOrder"></param>
public void SetOpenOrder(int openOrder)
{
if (canvas != null)
{
canvas.sortingOrder = openOrder;
}
}
/// <summary>
/// also would destroy panel gameObject
/// </summary>
public virtual void Dispose()
{
if (panelConfig.animationType == EAnimationMode.None)
{
GameObject.Destroy(gameObject);
return;
}
OnCloseEffectStart();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 607d7f7e1e2a44319bfc7d85788fd805
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 00f81d135c9e44ec4b4a064781b1bcd0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using UnityEngine;
namespace TapSDK.UI
{
[System.Serializable]
public struct BasePanelConfig
{
/// <summary>
/// animation effect related to opening and closing
/// </summary>
public EAnimationMode animationType;
public BasePanelConfig(EAnimationMode animationMode = EAnimationMode.None)
{
animationType = animationMode;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35a0b66e8719e4104bf30ae06108d41a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
namespace TapSDK.UI
{
public abstract class AbstractOpenPanelParameter
{
public virtual bool NeedConstantResolution => false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5b19f6b895fa4059a0e3b7aaf9581c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5124d8bb5646548db8970683b0a33abd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 765cb640e73334f7a9ccc676064c70be
folderAsset: yes
timeCreated: 1533284978
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
// -----------------------------------------------------------------------
// <copyright file="SimpleObjPool.cs" company="AillieoTech">
// Copyright (c) AillieoTech. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace TapSDK.UI.AillieoTech
{
using System;
using System.Collections.Generic;
public class SimpleObjPool<T>
{
private readonly Stack<T> stack;
private readonly Func<T> ctor;
private readonly Action<T> onRecycle;
private int size;
private int usedCount;
public SimpleObjPool(int max = 7, Action<T> onRecycle = null, Func<T> ctor = null)
{
this.stack = new Stack<T>(max);
this.size = max;
this.onRecycle = onRecycle;
this.ctor = ctor;
}
public T Get()
{
T item;
if (this.stack.Count == 0)
{
if (this.ctor != null)
{
item = this.ctor();
}
else
{
item = Activator.CreateInstance<T>();
}
}
else
{
item = this.stack.Pop();
}
this.usedCount++;
return item;
}
public void Recycle(T item)
{
if (this.onRecycle != null)
{
this.onRecycle.Invoke(item);
}
if (this.stack.Count < this.size)
{
this.stack.Push(item);
}
this.usedCount--;
}
public void Purge()
{
// TODO
}
public override string ToString()
{
return $"SimpleObjPool: item=[{typeof(T)}], inUse=[{this.usedCount}], restInPool=[{this.stack.Count}/{this.size}] ";
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 88625144948bd48d296916f8b0e9b41a
timeCreated: 1533284979
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ab23454192f22412c8ae6f7699645396
folderAsset: yes
timeCreated: 1533284978
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,828 @@
// -----------------------------------------------------------------------
// <copyright file="ScrollView.cs" company="AillieoTech">
// Copyright (c) AillieoTech. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace TapSDK.UI.AillieoTech
{
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
[DisallowMultipleComponent]
public class ScrollView : ScrollRect
{
[Tooltip("默认item尺寸")]
public Vector2 defaultItemSize;
[Tooltip("item的模板")]
public RectTransform itemTemplate;
// 0001
protected const int flagScrollDirection = 1;
[SerializeField]
[FormerlySerializedAs("m_layoutType")]
protected ItemLayoutType layoutType = ItemLayoutType.Vertical;
// 只保存4个临界index
protected int[] criticalItemIndex = new int[4];
// callbacks for items
protected Action<int, RectTransform> updateFunc;
protected Func<int, Vector2> itemSizeFunc;
protected Func<int> itemCountFunc;
protected Func<int, RectTransform> itemGetFunc;
protected Action<RectTransform> itemRecycleFunc;
private readonly List<ScrollItemWithRect> managedItems = new List<ScrollItemWithRect>();
private Rect refRect;
// resource management
private SimpleObjPool<RectTransform> itemPool = null;
private int dataCount = 0;
[Tooltip("初始化时池内item数量")]
[SerializeField]
private int poolSize;
// status
private bool initialized = false;
private int willUpdateData = 0;
private Vector3[] viewWorldConers = new Vector3[4];
private Vector3[] rectCorners = new Vector3[2];
// for hide and show
public enum ItemLayoutType
{
// 最后一位表示滚动方向
Vertical = 0b0001, // 0001
Horizontal = 0b0010, // 0010
VerticalThenHorizontal = 0b0100, // 0100
HorizontalThenVertical = 0b0101, // 0101
}
public virtual void SetUpdateFunc(Action<int, RectTransform> func)
{
this.updateFunc = func;
}
public virtual void SetItemSizeFunc(Func<int, Vector2> func)
{
this.itemSizeFunc = func;
}
public virtual void SetItemCountFunc(Func<int> func)
{
this.itemCountFunc = func;
}
public void SetItemGetAndRecycleFunc(Func<int, RectTransform> getFunc, Action<RectTransform> recycleFunc)
{
if (getFunc != null && recycleFunc != null)
{
this.itemGetFunc = getFunc;
this.itemRecycleFunc = recycleFunc;
}
else
{
this.itemGetFunc = null;
this.itemRecycleFunc = null;
}
}
public void ResetAllDelegates()
{
this.SetUpdateFunc(null);
this.SetItemSizeFunc(null);
this.SetItemCountFunc(null);
this.SetItemGetAndRecycleFunc(null, null);
}
public void UpdateData(bool immediately = true)
{
if (immediately)
{
this.willUpdateData |= 3; // 0011
this.InternalUpdateData();
}
else
{
if (this.willUpdateData == 0 && this.IsActive())
{
this.StartCoroutine(this.DelayUpdateData());
}
this.willUpdateData |= 3;
}
}
public void UpdateDataIncrementally(bool immediately = true)
{
if (immediately)
{
this.willUpdateData |= 1; // 0001
this.InternalUpdateData();
}
else
{
if (this.willUpdateData == 0)
{
this.StartCoroutine(this.DelayUpdateData());
}
this.willUpdateData |= 1;
}
}
public void ScrollTo(int index)
{
this.InternalScrollTo(index);
}
protected override void OnEnable()
{
base.OnEnable();
if (this.willUpdateData != 0)
{
this.StartCoroutine(this.DelayUpdateData());
}
}
protected override void OnDisable()
{
this.initialized = false;
base.OnDisable();
}
protected virtual void InternalScrollTo(int index)
{
index = Mathf.Clamp(index, 0, this.dataCount - 1);
this.EnsureItemRect(index);
Rect r = this.managedItems[index].rect;
var dir = (int)this.layoutType & flagScrollDirection;
if (dir == 1)
{
// vertical
var value = 1 - (-r.yMax / (this.content.sizeDelta.y - this.refRect.height));
this.SetNormalizedPosition(value, 1);
}
else
{
// horizontal
var value = r.xMin / (this.content.sizeDelta.x - this.refRect.width);
this.SetNormalizedPosition(value, 0);
}
}
protected override void SetContentAnchoredPosition(Vector2 position)
{
base.SetContentAnchoredPosition(position);
this.UpdateCriticalItems();
}
protected override void SetNormalizedPosition(float value, int axis)
{
base.SetNormalizedPosition(value, axis);
this.ResetCriticalItems();
}
protected void EnsureItemRect(int index)
{
if (!this.managedItems[index].rectDirty)
{
// 已经是干净的了
return;
}
ScrollItemWithRect firstItem = this.managedItems[0];
if (firstItem.rectDirty)
{
Vector2 firstSize = this.GetItemSize(0);
firstItem.rect = CreateWithLeftTopAndSize(Vector2.zero, firstSize);
firstItem.rectDirty = false;
}
// 当前item之前的最近的已更新的rect
var nearestClean = 0;
for (var i = index; i >= 0; --i)
{
if (!this.managedItems[i].rectDirty)
{
nearestClean = i;
break;
}
}
// 需要更新 从 nearestClean 到 index 的尺寸
Rect nearestCleanRect = this.managedItems[nearestClean].rect;
Vector2 curPos = GetLeftTop(nearestCleanRect);
Vector2 size = nearestCleanRect.size;
this.MovePos(ref curPos, size);
for (var i = nearestClean + 1; i <= index; i++)
{
size = this.GetItemSize(i);
this.managedItems[i].rect = CreateWithLeftTopAndSize(curPos, size);
this.managedItems[i].rectDirty = false;
this.MovePos(ref curPos, size);
}
var range = new Vector2(Mathf.Abs(curPos.x), Mathf.Abs(curPos.y));
switch (this.layoutType)
{
case ItemLayoutType.VerticalThenHorizontal:
range.x += size.x;
range.y = this.refRect.height;
break;
case ItemLayoutType.HorizontalThenVertical:
range.x = this.refRect.width;
if (curPos.x != 0)
{
range.y += size.y;
}
break;
default:
break;
}
this.content.sizeDelta = range;
}
protected override void OnDestroy()
{
if (this.itemPool != null)
{
this.itemPool.Purge();
}
}
protected Rect GetItemLocalRect(int index)
{
if (index >= 0 && index < this.dataCount)
{
this.EnsureItemRect(index);
return this.managedItems[index].rect;
}
return (Rect)default;
}
#if UNITY_EDITOR
protected override void OnValidate()
{
var dir = (int)this.layoutType & flagScrollDirection;
if (dir == 1)
{
// vertical
if (this.horizontalScrollbar != null)
{
this.horizontalScrollbar.gameObject.SetActive(false);
this.horizontalScrollbar = null;
}
}
else
{
// horizontal
if (this.verticalScrollbar != null)
{
this.verticalScrollbar.gameObject.SetActive(false);
this.verticalScrollbar = null;
}
}
base.OnValidate();
}
#endif
private static Vector2 GetLeftTop(Rect rect)
{
Vector2 ret = rect.position;
ret.y += rect.size.y;
return ret;
}
private static Rect CreateWithLeftTopAndSize(Vector2 leftTop, Vector2 size)
{
Vector2 leftBottom = leftTop - new Vector2(0, size.y);
return new Rect(leftBottom, size);
}
private IEnumerator DelayUpdateData()
{
yield return new WaitForEndOfFrame();
this.InternalUpdateData();
}
private void InternalUpdateData()
{
if (!this.IsActive())
{
this.willUpdateData |= 3;
return;
}
if (!this.initialized)
{
this.InitScrollView();
}
var newDataCount = 0;
var keepOldItems = (this.willUpdateData & 2) == 0;
if (this.itemCountFunc != null)
{
newDataCount = this.itemCountFunc();
}
if (newDataCount != this.managedItems.Count)
{
if (this.managedItems.Count < newDataCount)
{
// 增加
if (!keepOldItems)
{
foreach (var itemWithRect in this.managedItems)
{
// 重置所有rect
itemWithRect.rectDirty = true;
}
}
while (this.managedItems.Count < newDataCount)
{
this.managedItems.Add(new ScrollItemWithRect());
}
}
else
{
// 减少 保留空位 避免GC
for (int i = 0, count = this.managedItems.Count; i < count; ++i)
{
if (i < newDataCount)
{
// 重置所有rect
if (!keepOldItems)
{
this.managedItems[i].rectDirty = true;
}
if (i == newDataCount - 1)
{
this.managedItems[i].rectDirty = true;
}
}
// 超出部分 清理回收item
if (i >= newDataCount)
{
this.managedItems[i].rectDirty = true;
if (this.managedItems[i].item != null)
{
this.RecycleOldItem(this.managedItems[i].item);
this.managedItems[i].item = null;
}
}
}
}
}
else
{
if (!keepOldItems)
{
for (int i = 0, count = this.managedItems.Count; i < count; ++i)
{
// 重置所有rect
this.managedItems[i].rectDirty = true;
}
}
}
this.dataCount = newDataCount;
this.ResetCriticalItems();
this.willUpdateData = 0;
}
private void ResetCriticalItems()
{
bool hasItem, shouldShow;
int firstIndex = -1, lastIndex = -1;
for (var i = 0; i < this.dataCount; i++)
{
hasItem = this.managedItems[i].item != null;
shouldShow = this.ShouldItemSeenAtIndex(i);
if (shouldShow)
{
if (firstIndex == -1)
{
firstIndex = i;
}
lastIndex = i;
}
if (hasItem && shouldShow)
{
// 应显示且已显示
this.SetDataForItemAtIndex(this.managedItems[i].item, i);
continue;
}
if (hasItem == shouldShow)
{
// 不应显示且未显示
// if (firstIndex != -1)
// {
// // 已经遍历完所有要显示的了 后边的先跳过
// break;
// }
continue;
}
if (hasItem && !shouldShow)
{
// 不该显示 但是有
this.RecycleOldItem(this.managedItems[i].item);
this.managedItems[i].item = null;
continue;
}
if (shouldShow && !hasItem)
{
// 需要显示 但是没有
RectTransform item = this.GetNewItem(i);
this.OnGetItemForDataIndex(item, i);
this.managedItems[i].item = item;
continue;
}
}
// content.localPosition = Vector2.zero;
this.criticalItemIndex[CriticalItemType.UpToHide] = firstIndex;
this.criticalItemIndex[CriticalItemType.DownToHide] = lastIndex;
this.criticalItemIndex[CriticalItemType.UpToShow] = Mathf.Max(firstIndex - 1, 0);
this.criticalItemIndex[CriticalItemType.DownToShow] = Mathf.Min(lastIndex + 1, this.dataCount - 1);
}
private RectTransform GetCriticalItem(int type)
{
var index = this.criticalItemIndex[type];
if (index >= 0 && index < this.dataCount)
{
return this.managedItems[index].item;
}
return null;
}
private void UpdateCriticalItems()
{
var dirty = true;
while (dirty)
{
dirty = false;
for (int i = CriticalItemType.UpToHide; i <= CriticalItemType.DownToShow; i++)
{
if (i <= CriticalItemType.DownToHide)
{
// 隐藏离开可见区域的item
dirty = dirty || this.CheckAndHideItem(i);
}
else
{
// 显示进入可见区域的item
dirty = dirty || this.CheckAndShowItem(i);
}
}
}
}
private bool CheckAndHideItem(int criticalItemType)
{
RectTransform item = this.GetCriticalItem(criticalItemType);
var criticalIndex = this.criticalItemIndex[criticalItemType];
if (item != null && !this.ShouldItemSeenAtIndex(criticalIndex))
{
this.RecycleOldItem(item);
this.managedItems[criticalIndex].item = null;
if (criticalItemType == CriticalItemType.UpToHide)
{
// 最上隐藏了一个
this.criticalItemIndex[criticalItemType + 2] = Mathf.Max(criticalIndex, this.criticalItemIndex[criticalItemType + 2]);
this.criticalItemIndex[criticalItemType]++;
}
else
{
// 最下隐藏了一个
this.criticalItemIndex[criticalItemType + 2] = Mathf.Min(criticalIndex, this.criticalItemIndex[criticalItemType + 2]);
this.criticalItemIndex[criticalItemType]--;
}
this.criticalItemIndex[criticalItemType] = Mathf.Clamp(this.criticalItemIndex[criticalItemType], 0, this.dataCount - 1);
if (this.criticalItemIndex[CriticalItemType.UpToHide] > this.criticalItemIndex[CriticalItemType.DownToHide])
{
// 偶然的情况 拖拽超出一屏
this.ResetCriticalItems();
return false;
}
return true;
}
return false;
}
private bool CheckAndShowItem(int criticalItemType)
{
RectTransform item = this.GetCriticalItem(criticalItemType);
var criticalIndex = this.criticalItemIndex[criticalItemType];
if (item == null && this.ShouldItemSeenAtIndex(criticalIndex))
{
RectTransform newItem = this.GetNewItem(criticalIndex);
this.OnGetItemForDataIndex(newItem, criticalIndex);
this.managedItems[criticalIndex].item = newItem;
if (criticalItemType == CriticalItemType.UpToShow)
{
// 最上显示了一个
this.criticalItemIndex[criticalItemType - 2] = Mathf.Min(criticalIndex, this.criticalItemIndex[criticalItemType - 2]);
this.criticalItemIndex[criticalItemType]--;
}
else
{
// 最下显示了一个
this.criticalItemIndex[criticalItemType - 2] = Mathf.Max(criticalIndex, this.criticalItemIndex[criticalItemType - 2]);
this.criticalItemIndex[criticalItemType]++;
}
this.criticalItemIndex[criticalItemType] = Mathf.Clamp(this.criticalItemIndex[criticalItemType], 0, this.dataCount - 1);
if (this.criticalItemIndex[CriticalItemType.UpToShow] >= this.criticalItemIndex[CriticalItemType.DownToShow])
{
// 偶然的情况 拖拽超出一屏
this.ResetCriticalItems();
return false;
}
return true;
}
return false;
}
private bool ShouldItemSeenAtIndex(int index)
{
if (index < 0 || index >= this.dataCount)
{
return false;
}
this.EnsureItemRect(index);
return new Rect(this.refRect.position - this.content.anchoredPosition, this.refRect.size).Overlaps(this.managedItems[index].rect);
}
private bool ShouldItemFullySeenAtIndex(int index)
{
if (index < 0 || index >= this.dataCount)
{
return false;
}
this.EnsureItemRect(index);
return this.IsRectContains(new Rect(this.refRect.position - this.content.anchoredPosition, this.refRect.size), this.managedItems[index].rect);
}
private bool IsRectContains(Rect outRect, Rect inRect, bool bothDimensions = false)
{
if (bothDimensions)
{
var xContains = (outRect.xMax >= inRect.xMax) && (outRect.xMin <= inRect.xMin);
var yContains = (outRect.yMax >= inRect.yMax) && (outRect.yMin <= inRect.yMin);
return xContains && yContains;
}
else
{
var dir = (int)this.layoutType & flagScrollDirection;
if (dir == 1)
{
// 垂直滚动 只计算y向
return (outRect.yMax >= inRect.yMax) && (outRect.yMin <= inRect.yMin);
}
else
{
// = 0
// 水平滚动 只计算x向
return (outRect.xMax >= inRect.xMax) && (outRect.xMin <= inRect.xMin);
}
}
}
private void InitPool()
{
var poolNode = new GameObject("POOL");
poolNode.SetActive(false);
poolNode.transform.SetParent(this.transform, false);
this.itemPool = new SimpleObjPool<RectTransform>(
this.poolSize,
(RectTransform item) =>
{
item.transform.SetParent(poolNode.transform, false);
},
() =>
{
GameObject itemObj = Instantiate(this.itemTemplate.gameObject);
RectTransform item = itemObj.GetComponent<RectTransform>();
itemObj.transform.SetParent(poolNode.transform, false);
item.anchorMin = Vector2.up;
item.anchorMax = Vector2.up;
item.pivot = Vector2.zero;
itemObj.SetActive(true);
return item;
});
}
private void OnGetItemForDataIndex(RectTransform item, int index)
{
this.SetDataForItemAtIndex(item, index);
item.transform.SetParent(this.content, false);
}
private void SetDataForItemAtIndex(RectTransform item, int index)
{
if (this.updateFunc != null)
{
this.updateFunc(index, item);
}
this.SetPosForItemAtIndex(item, index);
}
private void SetPosForItemAtIndex(RectTransform item, int index)
{
this.EnsureItemRect(index);
Rect r = this.managedItems[index].rect;
item.localPosition = r.position;
item.sizeDelta = r.size;
}
private Vector2 GetItemSize(int index)
{
if (index >= 0 && index <= this.dataCount)
{
if (this.itemSizeFunc != null)
{
return this.itemSizeFunc(index);
}
}
return this.defaultItemSize;
}
private RectTransform GetNewItem(int index)
{
RectTransform item;
if (this.itemGetFunc != null)
{
item = this.itemGetFunc(index);
}
else
{
item = this.itemPool.Get();
}
return item;
}
private void RecycleOldItem(RectTransform item)
{
if (this.itemRecycleFunc != null)
{
this.itemRecycleFunc(item);
}
else
{
this.itemPool.Recycle(item);
}
}
private void InitScrollView()
{
this.initialized = true;
// 根据设置来控制原ScrollRect的滚动方向
var dir = (int)this.layoutType & flagScrollDirection;
this.vertical = dir == 1;
this.horizontal = dir == 0;
this.content.pivot = Vector2.up;
this.content.anchorMin = Vector2.up;
this.content.anchorMax = Vector2.up;
this.content.anchoredPosition = Vector2.zero;
this.InitPool();
this.UpdateRefRect();
}
// refRect是在Content节点下的 viewport的 rect
private void UpdateRefRect()
{
/*
* WorldCorners
*
* 1 ------- 2
* | |
* | |
* 0 ------- 3
*
*/
if (!CanvasUpdateRegistry.IsRebuildingLayout())
{
Canvas.ForceUpdateCanvases();
}
this.viewRect.GetWorldCorners(this.viewWorldConers);
this.rectCorners[0] = this.content.transform.InverseTransformPoint(this.viewWorldConers[0]);
this.rectCorners[1] = this.content.transform.InverseTransformPoint(this.viewWorldConers[2]);
this.refRect = new Rect((Vector2)this.rectCorners[0] - this.content.anchoredPosition, this.rectCorners[1] - this.rectCorners[0]);
}
private void MovePos(ref Vector2 pos, Vector2 size)
{
// 注意 所有的rect都是左下角为基准
switch (this.layoutType)
{
case ItemLayoutType.Vertical:
// 垂直方向 向下移动
pos.y -= size.y;
break;
case ItemLayoutType.Horizontal:
// 水平方向 向右移动
pos.x += size.x;
break;
case ItemLayoutType.VerticalThenHorizontal:
pos.y -= size.y;
if (pos.y <= -this.refRect.height)
{
pos.y = 0;
pos.x += size.x;
}
break;
case ItemLayoutType.HorizontalThenVertical:
pos.x += size.x;
if (pos.x >= this.refRect.width)
{
pos.x = 0;
pos.y -= size.y;
}
break;
default:
break;
}
}
// const int 代替 enum 减少 (int)和(CriticalItemType)转换
protected static class CriticalItemType
{
public static byte UpToHide = 0;
public static byte DownToHide = 1;
public static byte UpToShow = 2;
public static byte DownToShow = 3;
}
private class ScrollItemWithRect
{
// scroll item 身上的 RectTransform组件
public RectTransform item;
// scroll item 在scrollview中的位置
public Rect rect;
// rect 是否需要更新
public bool rectDirty = true;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bac7eb1be8f6f40459d388c929d1946f
timeCreated: 1533042733
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,245 @@
// -----------------------------------------------------------------------
// <copyright file="ScrollViewEx.cs" company="AillieoTech">
// Copyright (c) AillieoTech. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
namespace TapSDK.UI.AillieoTech
{
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
[RequireComponent(typeof(RectTransform))]
[DisallowMultipleComponent]
public class ScrollViewEx : ScrollView
{
[SerializeField]
[FormerlySerializedAs("m_pageSize")]
private int pageSize = 50;
private int startOffset = 0;
private Func<int> realItemCountFunc;
private Vector2 lastPosition;
private bool reloadFlag = false;
public override void SetUpdateFunc(Action<int, RectTransform> func)
{
if (func != null)
{
var f = func;
func = (index, rect) =>
{
f(index + this.startOffset, rect);
};
}
base.SetUpdateFunc(func);
}
public override void SetItemSizeFunc(Func<int, Vector2> func)
{
if (func != null)
{
var f = func;
func = (index) =>
{
return f(index + this.startOffset);
};
}
base.SetItemSizeFunc(func);
}
public override void SetItemCountFunc(Func<int> func)
{
this.realItemCountFunc = func;
if (func != null)
{
var f = func;
func = () => Mathf.Min(f(), this.pageSize);
}
base.SetItemCountFunc(func);
}
public override void OnDrag(PointerEventData eventData)
{
if (this.reloadFlag)
{
this.reloadFlag = false;
this.OnEndDrag(eventData);
this.OnBeginDrag(eventData);
return;
}
base.OnDrag(eventData);
}
protected override void Awake()
{
base.Awake();
this.lastPosition = Vector2.up;
this.onValueChanged.AddListener(this.OnValueChanged);
}
protected override void InternalScrollTo(int index)
{
var count = 0;
if (this.realItemCountFunc != null)
{
count = this.realItemCountFunc();
}
index = Mathf.Clamp(index, 0, count - 1);
this.startOffset = Mathf.Clamp(index - (this.pageSize / 2), 0, count - this.itemCountFunc());
this.UpdateData(true);
base.InternalScrollTo(index - this.startOffset);
}
private void OnValueChanged(Vector2 position)
{
int toShow;
int critical;
bool downward;
int pin;
Vector2 delta = position - this.lastPosition;
this.lastPosition = position;
this.reloadFlag = false;
if (((int)this.layoutType & flagScrollDirection) == 1)
{
// 垂直滚动 只计算y向
if (delta.y < 0)
{
// 向上
toShow = this.criticalItemIndex[CriticalItemType.DownToShow];
critical = this.pageSize - 1;
if (toShow < critical)
{
return;
}
pin = critical - 1;
downward = false;
}
else if (delta.y > 0)
{
// 向下
toShow = this.criticalItemIndex[CriticalItemType.UpToShow];
critical = 0;
if (toShow > critical)
{
return;
}
pin = critical + 1;
downward = true;
}
else
{
return;
}
}
else
{
// = 0
// 水平滚动 只计算x向
if (delta.x > 0)
{
// 向右
toShow = this.criticalItemIndex[CriticalItemType.UpToShow];
critical = 0;
if (toShow > critical)
{
return;
}
pin = critical + 1;
downward = true;
}
else if (delta.x < 0)
{
// 向左
toShow = this.criticalItemIndex[CriticalItemType.DownToShow];
critical = this.pageSize - 1;
if (toShow < critical)
{
return;
}
pin = critical - 1;
downward = false;
}
else
{
return;
}
}
// 该翻页了 翻半页吧
var old = this.startOffset;
if (downward)
{
this.startOffset -= this.pageSize / 2;
}
else
{
this.startOffset += this.pageSize / 2;
}
var realDataCount = 0;
if (this.realItemCountFunc != null)
{
realDataCount = this.realItemCountFunc();
}
this.startOffset = Mathf.Clamp(this.startOffset, 0, Mathf.Max(realDataCount - this.pageSize, 0));
if (old != this.startOffset)
{
this.reloadFlag = true;
// 记录 原先的速度
Vector2 oldVelocity = this.velocity;
// 计算 pin元素的世界坐标
Rect rect = this.GetItemLocalRect(pin);
Vector2 oldWorld = this.content.TransformPoint(rect.position);
var dataCount = 0;
if (this.itemCountFunc != null)
{
dataCount = this.itemCountFunc();
}
if (dataCount > 0)
{
this.EnsureItemRect(0);
if (dataCount > 1)
{
this.EnsureItemRect(dataCount - 1);
}
}
// 根据 pin元素的世界坐标 计算出content的position
var pin2 = pin + old - this.startOffset;
Rect rect2 = this.GetItemLocalRect(pin2);
Vector2 newWorld = this.content.TransformPoint(rect2.position);
Vector2 deltaWorld = newWorld - oldWorld;
Vector2 deltaLocal = this.content.InverseTransformVector(deltaWorld);
this.SetContentAnchoredPosition(this.content.anchoredPosition - deltaLocal);
this.UpdateData(true);
this.velocity = oldVelocity;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 17ff80d59e3504ef385ee40177f0b4a4
timeCreated: 1533042733
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49cacbc5313da4067862ab3b83239ae5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using TapSDK.Core.Internal.Log;
namespace TapSDK.Core.Internal.Utils {
public static class BridgeUtils {
public static bool IsSupportMobilePlatform => Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer;
public static bool IsSupportStandalonePlatform => Application.platform == RuntimePlatform.OSXPlayer ||
Application.platform == RuntimePlatform.WindowsPlayer ||
Application.platform == RuntimePlatform.LinuxPlayer;
public static object CreateBridgeImplementation(Type interfaceType, string startWith) {
// 跳过初始化直接使用 TapLoom会在子线程被TapSDK.Core.BridgeCallback.Invoke 初始化
TapLoom.Initialize();
// 获取所有程序集
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
// 查找以 startWith 开头的程序集
var matchingAssemblies = allAssemblies
.Where(assembly => assembly.GetName().FullName.StartsWith(startWith))
.ToList();
// 从匹配的程序集中查找实现指定接口的类
List<Type> allCandidateTypes = new List<Type>();
foreach (var assembly in matchingAssemblies) {
try {
var types = assembly.GetTypes()
.Where(type => type.IsClass && interfaceType.IsAssignableFrom(type))
.ToList();
foreach (var type in types) {
allCandidateTypes.Add(type);
}
}
catch (Exception ex) {
TapLog.Error($"[TapTap] 获取程序集 {assembly.GetName().Name} 中的类型时出错: {ex.Message}");
}
}
// 使用原始逻辑查找实现类
Type bridgeImplementationType = null;
try {
bridgeImplementationType = matchingAssemblies
.SelectMany(assembly => {
try {
return assembly.GetTypes();
} catch {
return Type.EmptyTypes;
}
})
.SingleOrDefault(clazz => interfaceType.IsAssignableFrom(clazz) && clazz.IsClass);
// 如果使用 SingleOrDefault 没找到,尝试使用 FirstOrDefault
if (bridgeImplementationType == null && allCandidateTypes.Count > 0) {
bridgeImplementationType = allCandidateTypes.FirstOrDefault();
}
}
catch (Exception ex) {
TapLog.Error($"[TapTap] 在查找实现类时发生异常: {ex.Message}\n{ex.StackTrace}");
}
if (bridgeImplementationType == null) {
// 尝试在所有程序集中查找实现(不限制命名空间前缀)
if (matchingAssemblies.Count == 0) {
List<Type> implementationsInAllAssemblies = new List<Type>();
foreach (var assembly in allAssemblies) {
try {
var types = assembly.GetTypes()
.Where(type => type.IsClass && !type.IsAbstract && interfaceType.IsAssignableFrom(type))
.ToList();
if (types.Count > 0) {
implementationsInAllAssemblies.AddRange(types);
}
}
catch { /* 忽略错误 */ }
}
}
return null;
}
try {
return Activator.CreateInstance(bridgeImplementationType);
}
catch (Exception ex) {
return null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56e7026e809cb4a4ead9bf6479d212a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using TapSDK.UI;
namespace TapSDK.Core.Internal.Utils
{
public sealed class EventManager : Singleton<EventManager>
{
public const string OnApplicationPause = "OnApplicationPause";
public const string OnApplicationQuit = "OnApplicationQuit";
public const string OnComplianceUserChanged = "OnComplianceUserChanged";
public const string OnTapUserChanged = "OnTapUserChanged";
public const string IsLaunchedFromTapTapPCFinished = "IsLaunchedFromTapTapPCFinished";
private Dictionary<string, Action<object>> eventRegistries = new Dictionary<string, Action<object>>();
public static void AddListener(string eventName, Action<object> listener) {
if (listener == null) return;
if (string.IsNullOrEmpty(eventName)) return;
Action<object> thisEvent;
if (Instance.eventRegistries.TryGetValue(eventName, out thisEvent)) {
thisEvent += listener;
Instance.eventRegistries[eventName] = thisEvent;
} else {
thisEvent += listener;
Instance.eventRegistries.Add(eventName, thisEvent);
}
}
public static void RemoveListener(string eventName, Action<object> listener) {
if (listener == null) return;
if (string.IsNullOrEmpty(eventName)) return;
Action<object> thisEvent;
if (Instance.eventRegistries.TryGetValue(eventName, out thisEvent)) {
thisEvent -= listener;
Instance.eventRegistries[eventName] = thisEvent;
}
}
public static void TriggerEvent(string eventName, object message) {
Action<object> thisEvent = null;
if (Instance.eventRegistries.TryGetValue(eventName, out thisEvent)) {
thisEvent.Invoke(message);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3283bdf1ebdf64e859e7bcff4043ac04
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Text;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using TapSDK.Core.Internal.Log;
namespace TapSDK.Core.Internal.Utils {
public class ImageUtils {
private readonly static string OldCacheDirName = "tap-cache";
private readonly static MD5 md5 = MD5.Create();
private readonly static Dictionary<string, WeakReference<Texture>> cachedTextures = new Dictionary<string, WeakReference<Texture>>();
public static async Task<Texture> LoadImage(string url, int timeout = 30, bool useMemoryCache = true) {
if (string.IsNullOrEmpty(url)) {
TapLog.Warning(string.Format($"ImageUtils Fetch image is null! url is null or empty!"));
return null;
}
if (cachedTextures.TryGetValue(url, out WeakReference<Texture> refTex) &&
refTex.TryGetTarget(out Texture tex)) {
// 从内存加载
return tex;
} else {
try {
// 从本地缓存加载
Texture cachedImage = await LoadCachedImaged(url, timeout);
if (useMemoryCache) {
cachedTextures[url] = new WeakReference<Texture>(cachedImage);
}
return cachedImage;
} catch (Exception e) {
TapLog.Warning(e.Message);
try {
// 从网络加载
Texture2D newTex = await FetchImage(url, timeout);
if (useMemoryCache) {
cachedTextures[url] = new WeakReference<Texture>(newTex);
}
// 缓存到本地
_ = CacheImage(url, newTex);
return newTex;
} catch (Exception ex) {
TapLog.Warning(ex.Message);
return null;
}
}
}
}
public static async Task<Texture2D> FetchImage(string url, int timeout = 30) {
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(url)) {
request.timeout = timeout;
UnityWebRequestAsyncOperation operation = request.SendWebRequest();
while (!operation.isDone) {
await Task.Delay(30);
}
if (request.isNetworkError || request.isHttpError) {
throw new Exception("Fetch image error.");
} else {
Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler)?.texture;
if (texture == null) {
TapLog.Warning($"ImageUtils Fetch image is null! url: {url}");
}
return texture;
}
}
}
static async Task<Texture> LoadCachedImaged(string url, int timeout = 30) {
string cachedImagePath = GetCachedPath(url);
if (!File.Exists(cachedImagePath)) {
throw new Exception("No cached image.");
}
string cachedImageUrl = $"file://{cachedImagePath}";
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(cachedImageUrl)) {
request.timeout = timeout;
UnityWebRequestAsyncOperation operation = request.SendWebRequest();
while (!operation.isDone) {
await Task.Delay(30);
}
if (request.isNetworkError || request.isHttpError) {
RemoveCachedImage(cachedImagePath);
throw new Exception("Load cache image error.");
} else {
var texture = ((DownloadHandlerTexture)request.downloadHandler)?.texture;
if (texture == null) {
RemoveCachedImage(cachedImagePath);
throw new Exception("Cached image is invalid.");
}
return texture;
}
}
}
static async Task CacheImage(string url, Texture2D tex) {
string cacheImagePath = GetCachedPath(url);
// 写入缓存
byte[] imageData = tex.EncodeToPNG();
using (FileStream fileStream = new FileStream(cacheImagePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) {
await fileStream.WriteAsync(imageData, 0, imageData.Length);
}
}
static void RemoveCachedImage(string cachedImagePath) {
try {
File.Delete(cachedImagePath);
} finally {
}
}
static string ToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.Length; i++) {
sb.Append(bytes[i].ToString("x2"));
}
return sb.ToString();
}
static string GetCachedPath(string url) {
string cachedHashName = ToHex(md5.ComputeHash(Encoding.UTF8.GetBytes(url)));
return Path.Combine(CacheDirPath, cachedHashName);
}
static string CacheDirPath {
get {
string newCacheDirPath = Path.Combine(Application.persistentDataPath, OldCacheDirName);
if (TapTapSDK.taptapSdkOptions != null && !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId) ){
newCacheDirPath = Path.Combine(Application.persistentDataPath, OldCacheDirName + "_" + TapTapSDK.taptapSdkOptions.clientId);
}
if(!Directory.Exists(newCacheDirPath)) {
string oldPath = Path.Combine(Application.persistentDataPath, OldCacheDirName);
if (Directory.Exists(oldPath)) {
Directory.Move(oldPath, newCacheDirPath);
}
}
if (!Directory.Exists(newCacheDirPath)) {
Directory.CreateDirectory(newCacheDirPath);
}
return newCacheDirPath;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 82045c1fa54ce4e6ab3e286669044f15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,241 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
namespace TapSDK.Core.Internal.Utils
{
public class TapLoom : MonoBehaviour
{
public static int maxThreads = 8;
static int numThreads;
private static TapLoom _current;
private int _count;
private bool isPause = false;
// 记录主线程 ID
private static int _mainThreadId = -1;
public static TapLoom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
_mainThreadId = Thread.CurrentThread.ManagedThreadId;
}
static bool initialized;
public static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("TapLoom");
DontDestroyOnLoad(g);
_current = g.AddComponent<TapLoom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread(action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(
new DelayedQueueItem { time = Time.time, action = action }
);
}
}
else
{
if (Current != null && Current._actions != null)
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
}
/// <summary>
/// 在线程池中执行任务,非主线程
/// </summary>
/// <param name="a"> 任务 </param>
/// <returns></returns>
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
/// <summary>
/// 阻塞式在主线程执行任务并返回值,当发生异常或超时时,返回默认值
/// </summary>
/// <param name="func"> 任务 </param>
/// <param name="defaultValue"> 默认值 </param>
/// <param name="timeout"> 超时时间,默认 100 毫秒</param>
/// <returns> 任务返回值或默认值 </returns>
public static object RunOnMainThreadSync(
Func<object> func,
object defaultValue,
int timeout = 100
)
{
// 主线程未就绪,直接返回默认值
if (_mainThreadId < 0)
{
return defaultValue;
}
// 已经在主线程,直接执行
if (Thread.CurrentThread.ManagedThreadId == _mainThreadId)
{
return func();
}
object result = defaultValue;
var evt = new ManualResetEvent(false);
try
{
QueueOnMainThread(() =>
{
try
{
result = func();
}
catch (Exception ex)
{
TapLogger.Error("RunOnMainThreadSync failed " + ex.Message);
}
finally
{
try
{
evt.Set();
}
catch (ObjectDisposedException)
{
// evt 已被释放,直接忽略
}
}
});
evt.WaitOne(timeout);
}
finally
{
evt.Dispose(); // WaitOne 返回后再 Dispose
}
return result;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch { }
finally
{
Interlocked.Decrement(ref numThreads);
}
}
void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start() { }
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
void Update()
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
foreach (var a in _currentActions)
{
a();
}
lock (_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
foreach (var item in _currentDelayed)
_delayed.Remove(item);
}
foreach (var delayed in _currentDelayed)
{
delayed.action();
}
}
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus && isPause == false)
{
isPause = true;
EventManager.TriggerEvent(EventManager.OnApplicationPause, true);
}
else if (!pauseStatus && isPause)
{
isPause = false;
EventManager.TriggerEvent(EventManager.OnApplicationPause, false);
}
}
private void OnApplicationQuit()
{
EventManager.TriggerEvent(EventManager.OnApplicationQuit, true);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b2d946237d1b489c8e6f14fd553878e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
using System;
using UnityEngine;
using UnityEngine.UI;
public class TapMessage : MonoBehaviour {
public enum Time
{
threeSecond,
twoSecond,
oneSecond
};
public enum Position
{
top,
bottom
};
public static void ShowMessage ( string msg, TapMessage.Position position, TapMessage.Time time )
{
//Load message prefab from resources folder
GameObject messagePrefab = Resources.Load ( "TapMessage" ) as GameObject;
//Get container object of message
GameObject containerObject = messagePrefab.gameObject.transform.GetChild ( 0 ).gameObject;
//Get text object
GameObject textObject = containerObject.gameObject.transform.GetChild ( 0 ).GetChild ( 0 ).gameObject;
//Get text property
Text msg_text = textObject.GetComponent<Text> ( );
//Set message to text ui
msg_text.text = msg;
//Set position of container object of message
SetPosition ( containerObject.GetComponent<RectTransform> ( ), position );
//Spawn message object with all changes
GameObject clone = Instantiate ( messagePrefab );
// Destroy clone of message object according to the time
RemoveClone ( clone, time );
}
private static void SetPosition ( RectTransform rectTransform, Position position )
{
if (position == Position.top)
{
rectTransform.anchorMin = new Vector2 ( 0.5f, 1f );
rectTransform.anchorMax = new Vector2 ( 0.5f, 1f );
rectTransform.anchoredPosition = new Vector3 ( 0.5f, -100f, 0 );
}
else
{
rectTransform.anchorMin = new Vector2 ( 0.5f, 0 );
rectTransform.anchorMax = new Vector2 ( 0.5f, 0 );
rectTransform.anchoredPosition = new Vector3 ( 0.5f, 100f, 0 );
}
}
private static void RemoveClone ( GameObject clone, Time time )
{
if (time == Time.oneSecond)
{
Destroy ( clone.gameObject, 1f );
}
else if (time == Time.twoSecond)
{
Destroy ( clone.gameObject, 2f );
}
else
{
Destroy ( clone.gameObject, 3f );
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 87a6eaa9efa41844890325132c22595a
timeCreated: 1532842822
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using TapSDK.Core.Internal.Log;
namespace TapSDK.Core.Internal.Utils
{
public class TapVerifyInitStateUtils
{
public static void ShowVerifyErrorMsg(string error, string errorMsg)
{
if (error != null || error.Length > 0)
{
TapMessage.ShowMessage(error, TapMessage.Position.bottom, TapMessage.Time.twoSecond);
}
if (errorMsg != null && errorMsg.Length > 0)
{
TapLog.Error(errorMsg);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2f1bb7b9efe304ef2a058482236a3e9e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Collections.Specialized;
namespace TapSDK.Core.Internal.Utils {
public class UrlUtils {
public static NameValueCollection ParseQueryString(string query) {
NameValueCollection nvc = new NameValueCollection();
if (query.StartsWith("?")) {
query = query.Substring(1);
}
foreach (var param in query.Split('&')) {
string[] pair = param.Split('=');
if (pair.Length == 2) {
string key = Uri.UnescapeDataString(pair[0]);
string value = Uri.UnescapeDataString(pair[1]);
nvc[key] = value;
}
}
return nvc;
}
public static string ToQueryString(NameValueCollection nvc) {
var array = (from key in nvc.AllKeys
from value in nvc.GetValues(key)
select $"{Uri.EscapeDataString(key)}={Uri.EscapeDataString(value)}"
).ToArray();
return string.Join("&", array);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 022260b0620b74f97971e1314da78c69
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b758418efc1d49e58b2aad6c3a456d4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using TapSDK.Core.Internal.Log;
using UnityEngine;
namespace TapSDK.Core
{
public static class DataStorage
{
private static Dictionary<string, string> dataCache;
private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
// 缓存 MacAddress避免多次获取
private static string cachedMacAddress;
public static void SaveString(string key, string value)
{
string storageKey = GenerateStorageKey(key);
SaveStringToCache(storageKey, value);
string encodeValue = EncodeString(value);
PlayerPrefs.SetString(storageKey, encodeValue);
}
public static string LoadString(string key)
{
string storageKey = GenerateStorageKey(key);
string value = LoadStringFromCache(storageKey);
if (!string.IsNullOrEmpty(value))
{
return value;
}
value = PlayerPrefs.HasKey(storageKey) ? DecodeString(PlayerPrefs.GetString(storageKey)) : null;
// 尝试从本地获取旧版本数据
if (value == null)
{
value = PlayerPrefs.HasKey(key) ? DecodeString(PlayerPrefs.GetString(key)) : null;
// 旧版本存在有效数据时,转储为新的 storageKey
if (value != null)
{
PlayerPrefs.SetString(storageKey, EncodeString(value));
PlayerPrefs.DeleteKey(key);
}
}
if (value != null)
{
SaveStringToCache(storageKey, value);
}
return value;
}
public static void RemoveCacheKey(string key)
{
if (!string.IsNullOrEmpty(key))
{
PlayerPrefs.DeleteKey(key);
PlayerPrefs.DeleteKey(GenerateStorageKey(key));
}
}
private static void SaveStringToCache(string key, string value)
{
if (dataCache == null)
{
dataCache = new Dictionary<string, string>();
}
if (dataCache.ContainsKey(key))
{
dataCache[key] = value;
}
else
{
dataCache.Add(key, value);
}
}
private static string LoadStringFromCache(string key)
{
if (dataCache == null)
{
dataCache = new Dictionary<string, string>();
}
return dataCache.ContainsKey(key) ? dataCache[key] : null;
}
private static string EncodeString(string encryptString)
{
if (Platform.IsAndroid() || Platform.IsIOS())
{
return encryptString;
}
try
{
byte[] rgbKey = Encoding.UTF8.GetBytes(GetMacAddress().Substring(0, 8));
byte[] rgbIV = Keys;
byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
MemoryStream mStream = new MemoryStream();
CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
cStream.Write(inputByteArray, 0, inputByteArray.Length);
cStream.FlushFinalBlock();
cStream.Close();
return Convert.ToBase64String(mStream.ToArray());
}
catch
{
return encryptString;
}
}
private static string DecodeString(string decryptString)
{
if (Platform.IsAndroid() || Platform.IsIOS())
{
return decryptString;
}
try
{
byte[] rgbKey = Encoding.UTF8.GetBytes(GetMacAddress().Substring(0, 8));
byte[] rgbIV = Keys;
byte[] inputByteArray = Convert.FromBase64String(decryptString);
DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
MemoryStream mStream = new MemoryStream();
CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
cStream.Write(inputByteArray, 0, inputByteArray.Length);
cStream.FlushFinalBlock();
cStream.Close();
return Encoding.UTF8.GetString(mStream.ToArray());
}
catch (Exception e)
{
TapLog.Log(e.Message);
return decryptString;
}
}
private static string GetMacAddress()
{
if (!string.IsNullOrEmpty(cachedMacAddress))
{
return cachedMacAddress;
}
string physicalAddress = "FFFFFFFFFFFF";
if (Platform.IsAndroid() || Platform.IsIOS())
{
return physicalAddress;
}
NetworkInterface[] nice = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adaper in nice)
{
if (adaper.Description == "en0")
{
physicalAddress = adaper.GetPhysicalAddress().ToString();
break;
}
else
{
physicalAddress = adaper.GetPhysicalAddress().ToString();
if (physicalAddress != "")
{
break;
};
}
}
cachedMacAddress = physicalAddress;
return physicalAddress;
}
// 生成存储在本地文件中 key
private static string GenerateStorageKey(string originKey){
if(TapTapSDK.taptapSdkOptions != null && !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId)){
return originKey + "_" + TapTapSDK.taptapSdkOptions.clientId;
}
return originKey;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32c169d8c12eb4e9598b83c11ad84f48
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
namespace TapSDK.Core {
public interface ITapPropertiesProxy {
string GetProperties();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c03335ad3e42d4092aa308d6503ce66a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,636 @@
/*
* Copyright (c) 2013 Calvin Rien
*
* Based on the JSON parser by Patrick van Bergen
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
*
* Simplified it so that it doesn't throw exceptions
* and can be used in Unity iPhone with maximum code stripping.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Globalization;
//Serializer,Serialize
namespace TapSDK.Core
{
// Example usage:
//
// using UnityEngine;
// using System.Collections;
// using System.Collections.Generic;
// using MiniJSON;
//
// public class MiniJSONTest : MonoBehaviour {
// void Start () {
// var jsonString = "{ \"array\": [1.44,2,3], " +
// "\"object\": {\"key1\":\"value1\", \"key2\":256}, " +
// "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " +
// "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " +
// "\"int\": 65536, " +
// "\"float\": 3.1415926, " +
// "\"bool\": true, " +
// "\"null\": null }";
//
// var dict = Json.Deserialize(jsonString) as Dictionary<string,object>;
//
// Debug.Log("deserialized: " + dict.GetType());
// Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]);
// Debug.Log("dict['string']: " + (string) dict["string"]);
// Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles
// Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs
// Debug.Log("dict['unicode']: " + (string) dict["unicode"]);
//
// var str = Json.Serialize(dict);
//
// Debug.Log("serialized: " + str);
// }
// }
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary.
/// All numbers are parsed to doubles.
/// </summary>
public static class Json
{
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An List&lt;object&gt;, a Dictionary&lt;string, object&gt;, a double, an integer,a string, null, true, or false</returns>
public static object Deserialize(string json)
{
// save the string for debug information
if (json == null)
{
return null;
}
return Parser.Parse(json);
}
sealed class Parser : IDisposable
{
const string WORD_BREAK = "{}[],:\"";
public static bool IsWordBreak(char c)
{
return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1;
}
enum TOKEN
{
NONE,
CURLY_OPEN,
CURLY_CLOSE,
SQUARED_OPEN,
SQUARED_CLOSE,
COLON,
COMMA,
STRING,
NUMBER,
TRUE,
FALSE,
NULL
};
StringReader json;
Parser(string jsonString)
{
json = new StringReader(jsonString);
}
public static object Parse(string jsonString)
{
using (var instance = new Parser(jsonString))
{
return instance.ParseValue();
}
}
public void Dispose()
{
json.Dispose();
json = null;
}
Dictionary<string, object> ParseObject()
{
Dictionary<string, object> table = new Dictionary<string, object>();
// ditch opening brace
json.Read();
// {
while (true)
{
switch (NextToken)
{
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.CURLY_CLOSE:
return table;
default:
// name
string name = ParseString();
if (name == null)
{
return null;
}
// :
if (NextToken != TOKEN.COLON)
{
return null;
}
// ditch the colon
json.Read();
// value
table[name] = ParseValue();
break;
}
}
}
List<object> ParseArray()
{
List<object> array = new List<object>();
// ditch opening bracket
json.Read();
// [
var parsing = true;
while (parsing)
{
TOKEN nextToken = NextToken;
switch (nextToken)
{
case TOKEN.NONE:
return null;
case TOKEN.COMMA:
continue;
case TOKEN.SQUARED_CLOSE:
parsing = false;
break;
default:
object value = ParseByToken(nextToken);
array.Add(value);
break;
}
}
return array;
}
object ParseValue()
{
TOKEN nextToken = NextToken;
return ParseByToken(nextToken);
}
object ParseByToken(TOKEN token)
{
switch (token)
{
case TOKEN.STRING:
return ParseString();
case TOKEN.NUMBER:
return ParseNumber();
case TOKEN.CURLY_OPEN:
return ParseObject();
case TOKEN.SQUARED_OPEN:
return ParseArray();
case TOKEN.TRUE:
return true;
case TOKEN.FALSE:
return false;
case TOKEN.NULL:
return null;
default:
return null;
}
}
string ParseString()
{
StringBuilder s = new StringBuilder();
char c;
// ditch opening quote
json.Read();
bool parsing = true;
while (parsing)
{
if (json.Peek() == -1)
{
parsing = false;
break;
}
c = NextChar;
switch (c)
{
case '"':
parsing = false;
break;
case '\\':
if (json.Peek() == -1)
{
parsing = false;
break;
}
c = NextChar;
switch (c)
{
case '"':
case '\\':
case '/':
s.Append(c);
break;
case 'b':
s.Append('\b');
break;
case 'f':
s.Append('\f');
break;
case 'n':
s.Append('\n');
break;
case 'r':
s.Append('\r');
break;
case 't':
s.Append('\t');
break;
case 'u':
var hex = new char[4];
for (int i = 0; i < 4; i++)
{
hex[i] = NextChar;
}
s.Append((char)Convert.ToInt32(new string(hex), 16));
break;
}
break;
default:
s.Append(c);
break;
}
}
return s.ToString();
}
object ParseNumber()
{
string number = NextWord;
if (number.IndexOf('.') == -1)
{
long parsedInt;
Int64.TryParse(number, NumberStyles.Number, CultureInfo.InvariantCulture, out parsedInt);
return parsedInt;
}
double parsedDouble;
Double.TryParse(number, NumberStyles.Number, CultureInfo.InvariantCulture, out parsedDouble);
return parsedDouble;
}
void EatWhitespace()
{
while (Char.IsWhiteSpace(PeekChar))
{
json.Read();
if (json.Peek() == -1)
{
break;
}
}
}
char PeekChar
{
get
{
return Convert.ToChar(json.Peek());
}
}
char NextChar
{
get
{
return Convert.ToChar(json.Read());
}
}
string NextWord
{
get
{
StringBuilder word = new StringBuilder();
while (!IsWordBreak(PeekChar))
{
word.Append(NextChar);
if (json.Peek() == -1)
{
break;
}
}
return word.ToString();
}
}
TOKEN NextToken
{
get
{
EatWhitespace();
if (json.Peek() == -1)
{
return TOKEN.NONE;
}
switch (PeekChar)
{
case '{':
return TOKEN.CURLY_OPEN;
case '}':
json.Read();
return TOKEN.CURLY_CLOSE;
case '[':
return TOKEN.SQUARED_OPEN;
case ']':
json.Read();
return TOKEN.SQUARED_CLOSE;
case ',':
json.Read();
return TOKEN.COMMA;
case '"':
return TOKEN.STRING;
case ':':
return TOKEN.COLON;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return TOKEN.NUMBER;
}
switch (NextWord)
{
case "false":
return TOKEN.FALSE;
case "true":
return TOKEN.TRUE;
case "null":
return TOKEN.NULL;
}
return TOKEN.NONE;
}
}
}
/// <summary>
/// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string
/// </summary>
/// <param name="json">A Dictionary&lt;string, object&gt; / List&lt;object&gt;</param>
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
public static string Serialize(object obj)
{
return Serializer.Serialize(obj);
}
sealed class Serializer
{
StringBuilder builder;
Serializer()
{
builder = new StringBuilder();
}
public static string Serialize(object obj)
{
var instance = new Serializer();
instance.SerializeValue(obj);
return instance.builder.ToString();
}
void SerializeValue(object value)
{
IList asList;
IDictionary asDict;
string asStr;
if (value == null)
{
builder.Append("null");
}
else if ((asStr = value as string) != null)
{
SerializeString(asStr);
}
else if (value is bool)
{
builder.Append((bool)value ? "true" : "false");
}
else if ((asList = value as IList) != null)
{
SerializeArray(asList);
}
else if ((asDict = value as IDictionary) != null)
{
SerializeObject(asDict);
}
else if (value is char)
{
SerializeString(new string((char)value, 1));
}
else
{
SerializeOther(value);
}
}
void SerializeObject(IDictionary obj)
{
bool first = true;
builder.Append('{');
foreach (object e in obj.Keys)
{
if (!first)
{
builder.Append(',');
}
SerializeString(e.ToString());
builder.Append(':');
SerializeValue(obj[e]);
first = false;
}
builder.Append('}');
}
void SerializeArray(IList anArray)
{
builder.Append('[');
bool first = true;
foreach (object obj in anArray)
{
if (!first)
{
builder.Append(',');
}
SerializeValue(obj);
first = false;
}
builder.Append(']');
}
void SerializeString(string str)
{
builder.Append('\"');
char[] charArray = str.ToCharArray();
foreach (var c in charArray)
{
switch (c)
{
case '"':
builder.Append("\\\"");
break;
case '\\':
builder.Append("\\\\");
break;
case '\b':
builder.Append("\\b");
break;
case '\f':
builder.Append("\\f");
break;
case '\n':
builder.Append("\\n");
break;
case '\r':
builder.Append("\\r");
break;
case '\t':
builder.Append("\\t");
break;
default:
int codepoint = Convert.ToInt32(c);
if ((codepoint >= 32) && (codepoint <= 126))
{
builder.Append(c);
}
else
{
builder.Append("\\u");
builder.Append(codepoint.ToString("x4"));
}
break;
}
}
builder.Append('\"');
}
void SerializeOther(object value)
{
// NOTE: decimals lose precision during serialization.
// They always have, I'm just letting you know.
// Previously floats and doubles lost precision too.
if (value is float)
{
builder.Append(((float)value).ToString("R", CultureInfo.InvariantCulture));
}
else if (value is int
|| value is uint
|| value is long
|| value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is ulong)
{
builder.Append(value);
}
else if (value is double
|| value is decimal)
{
builder.Append(Convert.ToDouble(value).ToString("R", CultureInfo.InvariantCulture));
}
else
{
SerializeString(value.ToString());
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03626f52014bc4f14b549e8bb318ca74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 50f5141d913874984bd6a97cace57cbb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
namespace TapSDK.Core {
public enum TapLogLevel {
Debug,
Warn,
Error,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1456af90a56064fe9afe2ccd9692f261
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
using System;
using System.Text;
namespace TapSDK.Core {
public class TapLogger {
/// <summary>
/// Configures the logger.
/// </summary>
/// <value>The log delegate.</value>
public static Action<TapLogLevel, string> LogDelegate {
internal get; set;
}
public static void Debug(string log) {
LogDelegate?.Invoke(TapLogLevel.Debug, log);
}
public static void Debug(string format, params object[] args) {
LogDelegate?.Invoke(TapLogLevel.Debug, string.Format(format, args));
}
public static void Warn(string log) {
LogDelegate?.Invoke(TapLogLevel.Warn, log);
}
public static void Warn(string format, params object[] args) {
LogDelegate?.Invoke(TapLogLevel.Warn, string.Format(format, args));
}
public static void Error(string log) {
LogDelegate?.Invoke(TapLogLevel.Error, log);
}
public static void Error(string format, params object[] args) {
LogDelegate?.Invoke(TapLogLevel.Error, string.Format(format, args));
}
public static void Error(Exception e) {
StringBuilder sb = new StringBuilder();
sb.Append(e.GetType());
sb.Append("\n");
sb.Append(e.Message);
sb.Append("\n");
sb.Append(e.StackTrace);
Error(sb.ToString());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 256e6b07da183466a9621d24df1e3ae7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using UnityEngine;
namespace TapSDK.Core
{
public class Platform
{
public static bool IsAndroid()
{
return Application.platform == RuntimePlatform.Android;
}
public static bool IsIOS()
{
return Application.platform == RuntimePlatform.IPhonePlayer;
}
public static bool IsWin32()
{
return Application.platform == RuntimePlatform.WindowsPlayer;
}
public static bool IsMacOS()
{
return Application.platform == RuntimePlatform.OSXPlayer;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef69730c212ed4cc9b4f39d224e60de5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
namespace TapSDK.Core
{
public enum RegionType : int
{
CN = 0,
IO = 1
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77cbd92bb921740e7b83329a483a1221
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace TapSDK.Core
{
public static class SafeDictionary
{
public static T GetValue<T> (Dictionary<string, object> dic, string key, T defaultVal = default(T))
{
if (dic == null || dic.Keys.Count == 0) return default(T);
if (!dic.TryGetValue(key, out var outputValue))
return defaultVal;
if(typeof(T) == typeof(int)){
return (T)(object)int.Parse(outputValue.ToString());
}
if(typeof(T) == typeof(double)){
return (T)(object)double.Parse(outputValue.ToString());
}
if(typeof(T) == typeof(long)){
return (T)(object)long.Parse(outputValue.ToString());
}
if(typeof(T) == typeof(bool)){
return (T)outputValue;
}
return (T) outputValue;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c5d3a73d3e1d4bf890c3a0b14b560fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
using Newtonsoft.Json;
namespace TapSDK.Core
{
public class TapEngineBridgeResult
{
public const int RESULT_SUCCESS = 0;
public const int RESULT_ERROR = -1;
[JsonProperty("code")] public int code { get; set; }
[JsonProperty("message")] public string message { get; set; }
[JsonProperty("content")] public string content { get; set; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2ec1ea022bd44fcba1ad7c0fa5af9401
timeCreated: 1752474613

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
namespace TapSDK.Core
{
public class TapError
{
public int code;
public string errorDescription;
public TapError(string json)
{
if (string.IsNullOrEmpty(json))
{
return;
}
var dic = Json.Deserialize(json) as Dictionary<string, object>;
code = SafeDictionary.GetValue<int>(dic, "code");
errorDescription = SafeDictionary.GetValue<string>(dic, "error_description");
}
public TapError()
{
}
public static TapError SafeConstructorTapError(string json)
{
return string.IsNullOrEmpty(json) ? null : new TapError(json);
}
public static TapError UndefinedError()
{
return new TapError(TapErrorCode.ERROR_CODE_UNDEFINED, "UnKnown Error");
}
public static TapError LoginCancelError()
{
return new TapError(TapErrorCode.ERROR_CODE_LOGIN_CANCEL, "Login Cancel");
}
public TapError(int code, string errorDescription)
{
this.code = code;
this.errorDescription = errorDescription;
}
private static TapErrorCode ParseCode(int parseCode)
{
return Enum.IsDefined(typeof(TapErrorCode), parseCode)
? (TapErrorCode) Enum.ToObject(typeof(TapErrorCode), parseCode)
: TapErrorCode.ERROR_CODE_UNDEFINED;
}
public TapError(TapErrorCode code, string errorDescription)
{
this.code = (int) code;
this.errorDescription = errorDescription;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c93fd38e60af46b78131cf90dddff5e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
namespace TapSDK.Core
{
public enum TapErrorCode
{
/*
* 未知错误
*/
ERROR_CODE_UNDEFINED = 80000,
/**
* SDK 未初始化
*/
ERROR_CODE_UNINITIALIZED = 80001,
/**
* 绑定取消
*/
ERROR_CODE_BIND_CANCEL = 80002,
/**
* 绑定错误
*/
ERROR_CODE_BIND_ERROR = 80003,
/**
* 登陆错误
*/
ERROR_CODE_LOGOUT_INVALID_LOGIN_STATE = 80004,
/**
* 登陆被踢出
*/
ERROR_CODE_LOGOUT_KICKED = 80007,
/**
* 桥接回调错误
*/
ERROR_CODE_BRIDGE_EXECUTE = 80080,
/**
* 登录取消
*/
ERROR_CODE_LOGIN_CANCEL = 80081
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28bd88a8759b74f55a9e42d04f7aacf3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using System;
namespace TapSDK.Core
{
public class TapException : Exception
{
public int code;
public int Code {
get => code;
set {
code = value;
}
}
public string message;
public TapException(int code, string message) : base(message)
{
this.code = code;
this.message = message;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8258d80e38ee46738be23d42027f05c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More