H5、推送SDK
This commit is contained in:
parent
c25bcd63e9
commit
a71b171e16
|
@ -0,0 +1,33 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace EFSDK
|
||||
{
|
||||
public static class AutoSetEFSdk
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
public static void SetupCommunicationObject()
|
||||
{
|
||||
try
|
||||
{
|
||||
GameObject communicationObject = new GameObject("EFSdkAndroid");
|
||||
if (communicationObject != null)
|
||||
{
|
||||
EFSdkAndroid communicationComponent = communicationObject.AddComponent<EFSdkAndroid>();
|
||||
if (communicationComponent == null)
|
||||
{
|
||||
Debug.LogError("Failed to add EFSdkAndroid component to the GameObject.");
|
||||
}
|
||||
Object.DontDestroyOnLoad(communicationObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Failed to create the EFSdkAndroid GameObject.");
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"An error occurred while setting up the communication object: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9937d38cb2014e1e94087f6216eacd62
|
||||
timeCreated: 1742202739
|
|
@ -0,0 +1,433 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EFSDK
|
||||
{
|
||||
/// <summary>
|
||||
/// TKG Native SDK Android platform interface call
|
||||
/// </summary>
|
||||
public class EFSdk
|
||||
{
|
||||
private static EFSdk _mEfSdk;
|
||||
private static string mappingInfo = @"{""items"":[{""key"":""_sdk_float_balloon.png"",""value"":""aoa38ay.png""}]}";
|
||||
|
||||
|
||||
public static EFSdk get()
|
||||
{
|
||||
if (_mEfSdk == null)
|
||||
{
|
||||
_mEfSdk = new EFSdk();
|
||||
}
|
||||
|
||||
return _mEfSdk;
|
||||
}
|
||||
|
||||
|
||||
private AndroidJavaObject jo;
|
||||
|
||||
public EFSdk()
|
||||
{
|
||||
// java interface class
|
||||
using (AndroidJavaClass jc = new AndroidJavaClass("com.earn.push._SDK"))
|
||||
{
|
||||
jo = jc.GetStatic<AndroidJavaObject>("INSTANCE");
|
||||
}
|
||||
}
|
||||
|
||||
private T SDKCall<T>(string _method, params object[] _param)
|
||||
{
|
||||
try
|
||||
{
|
||||
return jo.Call<T>(_method, _param);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
private void SDKCall(string _method, params object[] _param)
|
||||
{
|
||||
try
|
||||
{
|
||||
jo.Call(_method, _param);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ActionType
|
||||
{
|
||||
COIN_CLICK, //点击金币
|
||||
BALLOON_CLICK, //点击气球
|
||||
COIN_SHOW, //金币展示出来了
|
||||
BOX_SHOW, //气球/宝箱展示出来了
|
||||
GAM_LOAD_SUCC, //GAM页面加载成功
|
||||
ON_RESUME, //游戏可见时回调,
|
||||
// CAN_GOBACK, //游戏可见时回调,
|
||||
}
|
||||
|
||||
public Action<ActionType, string> ActionCallback;
|
||||
public Action<string, Dictionary<string, string>> ActionSDKEventCallback;
|
||||
public Action<string> HdH5ImpressionAction;
|
||||
public Action<bool> mCanGobackAction;
|
||||
public Action<bool> mReqNotifyPermissionAction;
|
||||
|
||||
/// <summary>
|
||||
/// 在Init方法之后调用这个方法,设置SDK上报事件回调, 将SDK传过来的事件上报到Firebase,数数等
|
||||
/// </summary>
|
||||
/// <param name="actionCallbvack">
|
||||
/// 事件ID,事件属性
|
||||
/// </param>
|
||||
public void SetSDKEventCallback(Action<string, Dictionary<string, string>> eventKeyDict)
|
||||
{
|
||||
ActionSDKEventCallback = eventKeyDict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 互动广告展示回调,此时可以计算上报互动广告展示次数和收益
|
||||
/// </summary>
|
||||
/// <param name="callback">string 是互动广告的url</param>
|
||||
public void SetHdH5ImpressionCallback(Action<string> callback)
|
||||
{
|
||||
HdH5ImpressionAction = callback;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="actionCallbvack">ActionType 回调类型 ; string msg </param>
|
||||
public void Init(Action<ActionType, string> actionCallbvack)
|
||||
{
|
||||
ActionCallback = actionCallbvack;
|
||||
SDKInit();
|
||||
}
|
||||
|
||||
private void SDKInit()
|
||||
{
|
||||
// SDKCall("init");
|
||||
ActionCallback?.Invoke(ActionType.GAM_LOAD_SUCC, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展示WebView
|
||||
/// </summary>
|
||||
/// <param name="id">标签id</param>
|
||||
/// <param name="url">网址</param>
|
||||
/// <param name="pRect"></param>
|
||||
/// <param name="pCam"></param>
|
||||
public void ShowWebView(int id, string url, RectTransform pRect, Camera pCam = null)
|
||||
{
|
||||
Vector3[] tWorldCorners = new Vector3[4];
|
||||
pRect.GetWorldCorners(tWorldCorners);
|
||||
Vector2 tTopLeft = RectTransformUtility.WorldToScreenPoint(pCam, tWorldCorners[1]);
|
||||
Vector2 tBottomRight = RectTransformUtility.WorldToScreenPoint(pCam, tWorldCorners[3]);
|
||||
int tWidth = (int)Mathf.Abs(tBottomRight.x - tTopLeft.x);
|
||||
int tHeight = (int)Mathf.Abs(tBottomRight.y - tTopLeft.y);
|
||||
SDKCall("showWebViewToActivity", id, url, (int)tTopLeft.x, (int)(Screen.height - tTopLeft.y), tWidth,
|
||||
tHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除所有原生View, 回到游戏时调用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void RemoveAll()
|
||||
{
|
||||
SDKCall("removeAll");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新当前页面
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Refresh()
|
||||
{
|
||||
SDKCall("refresh");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回上一页
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void GoBack()
|
||||
{
|
||||
SDKCall("goBack");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回首页
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void GoHome()
|
||||
{
|
||||
SDKCall("goHome");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否手动控制漂浮道具显示/隐藏
|
||||
/// SDK内默认当H5页面加载完成后自动显示漂浮道具
|
||||
/// </summary>
|
||||
/// <param name="autoShow">true: 自动显示/隐藏道具 false: 游戏主动控制道具显示/隐藏</param>
|
||||
/// <returns></returns>
|
||||
public void AutoShowFloat(bool autoShow)
|
||||
{
|
||||
SDKCall("autoShowFloat", autoShow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 飘金币
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public void ShowFloatCoin(int id)
|
||||
{
|
||||
SDKCall("showFloatCoin", id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 飘金币
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="res">悬浮金币按钮的图片资源,传字符串 0 或 1 0:金币图 1:红点宝箱图 </param>
|
||||
/// <returns></returns>
|
||||
public void ShowFloatCoin(int id, String res)
|
||||
{
|
||||
SDKCall("showFloatCoin", id, res);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置悬浮金币按钮的图片资源
|
||||
/// </summary>
|
||||
/// <param name="res">传字符串 0 或 1 0:金币图 1:红点宝箱图</param>
|
||||
public void SetFloatCoinRes(String res)
|
||||
{
|
||||
SDKCall("setFloatCoinRes", res);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏金币
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void HideFloatCoin()
|
||||
{
|
||||
SDKCall("hideFloatCoin");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 飘气球
|
||||
/// </summary>
|
||||
/// <param name="startId"></param>
|
||||
/// <param name="endId"></param>
|
||||
/// <param name="fly_first_time"></param>
|
||||
/// <param name="fly_gap_time"></param>
|
||||
/// <returns></returns>
|
||||
public void ShowBalloon(int startId, int endId, int fly_first_time, int fly_gap_time)
|
||||
{
|
||||
SDKCall("showBalloon", startId, endId, fly_first_time, fly_gap_time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏气球
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void HideBalloon()
|
||||
{
|
||||
SDKCall("hideBalloon");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void ShowToast(string message)
|
||||
{
|
||||
SDKCall("showToast", message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前网页是否还能返回上一页, true:可以返回,此时页面不在首页 false: 不能返回了,当前页面就在首页
|
||||
/// </summary>
|
||||
public void CanGoback(Action<bool> canGobackAction)
|
||||
{
|
||||
mCanGobackAction = canGobackAction;
|
||||
SDKCall("canGoback");
|
||||
}
|
||||
|
||||
#region 推送通知
|
||||
|
||||
/// <summary>
|
||||
/// 满足条件:未领取 R$0.1 的 买量用户, 调用这个方法
|
||||
/// </summary>
|
||||
public void SubscribeUnclaimed01()
|
||||
{
|
||||
SDKCall("subscribeUnclaimed01");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不满足条件:未领取 R$0.1 的 买量用户, 调用这个方法
|
||||
/// </summary>
|
||||
public void UnSubscribeUnclaimed01()
|
||||
{
|
||||
SDKCall("unSubscribeUnclaimed01");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// 满足条件: 在排队中 且 当日R$1 未领取 的买量用户, 调用这个方法
|
||||
/// </summary>
|
||||
public void SubscribePending1()
|
||||
{
|
||||
SDKCall("subscribePending1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不满足条件: 在排队中 且 当日R$1 未领取 的买量用户, 调用这个方法
|
||||
/// </summary>
|
||||
public void UnSubscribePending1()
|
||||
{
|
||||
SDKCall("unSubscribePending1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订阅Firebase推送主题
|
||||
/// </summary>
|
||||
/// <param name="topic">主题名称</param>
|
||||
public void SubscribeToTopic(string topic)
|
||||
{
|
||||
SDKCall("subscribeToTopic", topic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消订阅Firebase推送主题
|
||||
/// </summary>
|
||||
/// <param name="topic">主题名称</param>
|
||||
public void UnSubscribeToTopic(string topic)
|
||||
{
|
||||
SDKCall("UnSubscribeToTopic", topic);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 向SDK上报当前金币总数,每次金币变化都要调用一次
|
||||
/// </summary>
|
||||
/// <param name="totalGold"></param>
|
||||
public void SendTotalGold2SDK(int totalGold)
|
||||
{
|
||||
SDKCall("setGoldNum", totalGold.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向SDK上报当前要提现的现金额,每次变化都要调用一次
|
||||
/// </summary>
|
||||
/// <param name="cashNum"></param>
|
||||
public void SendCashNum2SDK(double cashNum)
|
||||
{
|
||||
SDKCall("setCashNum", cashNum.ToString("0.00"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向SDK上报 游戏名字(当前语言的),每次语言变化都上报
|
||||
/// </summary>
|
||||
/// <param name="gameName"></param>
|
||||
public void SetGameName(string gameName)
|
||||
{
|
||||
SDKCall("setGameName", gameName);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 设置推送 消息通知 的文案
|
||||
// /// </summary>
|
||||
// /// <param name="message"></param>
|
||||
// public void SetCommPushMessage(string message)
|
||||
// {
|
||||
// SDKCall("setCommPushMessage", message);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前游戏语言是否是 西班牙语
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="isEs">西班牙语传 true, 其他的都传 false </param>
|
||||
public void SetCurrLang(bool isEs)
|
||||
{
|
||||
SDKCall("setCurrLang", isEs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前是否有通知权限
|
||||
/// </summary>
|
||||
public bool HasNotifyPermission()
|
||||
{
|
||||
return SDKCall<bool>("hasNotifyPermission");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求获取通知权限
|
||||
/// </summary>
|
||||
public void ReqNotifyPermission()
|
||||
{
|
||||
SDKCall("reqNotifyPermission");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求获取通知权限
|
||||
/// <param name="action">授权弹窗关闭回调 bool:表示用户是否允许了权限 true:有权限 false:无权限</param>
|
||||
/// </summary>
|
||||
public void ReqNotifyPermission(Action<bool> action)
|
||||
{
|
||||
mReqNotifyPermissionAction = action;
|
||||
SDKCall("reqNotifyPermission");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置推送开关, SDK默认关闭通知
|
||||
/// </summary>
|
||||
/// <param name="isOpen"></param>
|
||||
public void SetPushSwitch(bool isOpen)
|
||||
{
|
||||
SDKCall("pushSwitch", isOpen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息类通知弹出间隔设置为60秒(在线参数控制)-Key: messagenotif Value:60
|
||||
/// </summary>
|
||||
/// <param name="timeSeconds"></param>
|
||||
public void SetPushMessagenotif(int timeSeconds)
|
||||
{
|
||||
SDKCall("setPushMessagenotif", timeSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 持续性通知在进入游戏时弹出的时间间隔设置为300秒(在线参数控制 )-Key:persistentnotif Value:300
|
||||
/// </summary>
|
||||
/// <param name="timeSeconds"></param>
|
||||
public void SetPushPersistentnotif(int timeSeconds)
|
||||
{
|
||||
SDKCall("setPushPersistentnotif", timeSeconds);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 每次回调游戏的onResume的时候都调用一次,获取游戏要跳转的页面
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 0 不需要进行任何跳转
|
||||
/// 1 进行游戏主页
|
||||
/// 2 进入游戏的金币提现界面
|
||||
/// 3 进入对应小游戏1界面
|
||||
/// 4 进入对应小游戏2界面
|
||||
/// </returns>
|
||||
public int GetJumpPage()
|
||||
{
|
||||
return SDKCall<int>("getJumpPage");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3cf9c70ade0a42e08c9ea06733912dd2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,109 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace EFSDK
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
public class EFSdkAndroid : MonoBehaviour
|
||||
{
|
||||
private string COIN_CLICK = "coin_click";
|
||||
private string BALLOON_CLICK = "balloon_click";
|
||||
private string Coin_Show = "Coin_Show";
|
||||
private string Box_Show = "Box_Show";
|
||||
private string Gam_Load_Succ = "Gam_Load_Succ";
|
||||
private string On_Resume = "onResume";
|
||||
private string Can_Goback = "canGoback";
|
||||
|
||||
public void OnReceiverAnd(string message)
|
||||
{
|
||||
WLoom.QueueOnMainThread(_ =>
|
||||
{
|
||||
Debug.Log("Received message from Android: " + message);
|
||||
if (message.Contains(On_Resume))
|
||||
{
|
||||
EFSdk.get().ActionCallback?.Invoke(EFSdk.ActionType.ON_RESUME, message);
|
||||
}
|
||||
if (message.Contains(Can_Goback))
|
||||
{
|
||||
EFSdk.get().mCanGobackAction?.Invoke(bool.Parse(message.Split('#')[1]));
|
||||
}
|
||||
|
||||
if (BALLOON_CLICK.Equals(message))
|
||||
{
|
||||
//点击气球
|
||||
EFSdk.get().ActionCallback?.Invoke(EFSdk.ActionType.BALLOON_CLICK, message);
|
||||
}
|
||||
|
||||
if (Coin_Show.Equals(message))
|
||||
{
|
||||
//金币展示出来了
|
||||
EFSdk.get().ActionCallback?.Invoke(EFSdk.ActionType.COIN_SHOW, message);
|
||||
}
|
||||
if (COIN_CLICK.Equals(message))
|
||||
{
|
||||
//金币点击
|
||||
EFSdk.get().ActionCallback?.Invoke(EFSdk.ActionType.COIN_CLICK, message);
|
||||
}
|
||||
|
||||
if (Box_Show.Equals(message))
|
||||
{
|
||||
//宝箱展示出来了
|
||||
EFSdk.get().ActionCallback?.Invoke(EFSdk.ActionType.BOX_SHOW, message);
|
||||
}
|
||||
|
||||
if (message.Contains(Gam_Load_Succ))
|
||||
{
|
||||
//GAM页面加载成功 Gam_Load_Succ@id
|
||||
string[] parts = message.Split('@');
|
||||
EFSdk.get().ActionCallback?.Invoke(EFSdk.ActionType.GAM_LOAD_SUCC, parts[1]);
|
||||
}
|
||||
|
||||
if (message.StartsWith("reqNotifyPermission#"))
|
||||
{
|
||||
string[] flag = message.Split('#');
|
||||
EFSdk.get().mReqNotifyPermissionAction?.Invoke(flag[1].Equals("1"));
|
||||
}
|
||||
|
||||
if (message.StartsWith("Event#"))
|
||||
{
|
||||
string[] eventKeys = message.Split('#');
|
||||
if (eventKeys.Length > 0)
|
||||
{
|
||||
if (message.Contains("hd_h5_impression"))
|
||||
{
|
||||
//互动广告展示
|
||||
string url = eventKeys[2];
|
||||
EFSdk.get().HdH5ImpressionAction?.Invoke(url);
|
||||
}
|
||||
else if (eventKeys.Length == 2)
|
||||
{
|
||||
// 只有一个事件key
|
||||
string eventKey = eventKeys[1];
|
||||
EFSdk.get().ActionSDKEventCallback?.Invoke(eventKey, null);
|
||||
}
|
||||
else if (eventKeys.Length == 3)
|
||||
{
|
||||
// key-value事件
|
||||
string eventKey = eventKeys[1];
|
||||
string value = eventKeys[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
//多属性事件
|
||||
string eventKey = eventKeys[1];
|
||||
Dictionary<string, string> attrs = new Dictionary<string, string>();
|
||||
for (int i = 2; i < eventKeys.Length - 1; i += 2)
|
||||
{
|
||||
string key = eventKeys[i];
|
||||
string value = eventKeys[i + 1];
|
||||
attrs[key] = value;
|
||||
}
|
||||
|
||||
EFSdk.get().ActionSDKEventCallback?.Invoke(eventKey, attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 40a70c7c94e6494d947902b87c809b30
|
||||
timeCreated: 1742202547
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e4d0b212325940748840950d8c400f9f
|
||||
timeCreated: 1756628120
|
|
@ -0,0 +1,162 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using IOCompression = System.IO.Compression;
|
||||
|
||||
namespace EFSDK
|
||||
{
|
||||
public class AndroidResAarBuilder
|
||||
{
|
||||
private static readonly string ResDir = "Assets/StreamingAssets/Android/res";
|
||||
private static readonly string OutputDir = "Assets/Plugins/Android";
|
||||
private static readonly string TempDir = "Temp/AndroidResAar";
|
||||
private static readonly string EFSdk_FILE = "Assets/EFSDK/EFSdk.cs";
|
||||
|
||||
[MenuItem("EFSDK/Build Android Res AAR")]
|
||||
public static void BuildAAR()
|
||||
{
|
||||
if (!Directory.Exists(ResDir))
|
||||
{
|
||||
Debug.LogError($"Res folder not found: {ResDir}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理临时目录
|
||||
if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true);
|
||||
Directory.CreateDirectory(TempDir);
|
||||
|
||||
// 复制资源并重命名
|
||||
CopyAndRenameFiles(ResDir, TempDir, out Dictionary<string, string> mapping);
|
||||
string manifestPath = Path.Combine(TempDir, "AndroidManifest.xml");
|
||||
File.WriteAllText(manifestPath,
|
||||
@"<manifest xmlns:android=""http://schemas.android.com/apk/res/android""
|
||||
package=""com.unity.reswrapper"">
|
||||
<application/>
|
||||
</manifest>");
|
||||
|
||||
// 打包 AAR
|
||||
string aarPath = Path.Combine(OutputDir, "efsdk_res.aar");
|
||||
if (!Directory.Exists(OutputDir)) Directory.CreateDirectory(OutputDir);
|
||||
if (File.Exists(aarPath)) File.Delete(aarPath);
|
||||
|
||||
IOCompression.ZipFile.CreateFromDirectory(TempDir, aarPath, IOCompression.CompressionLevel.Optimal, false);
|
||||
Debug.Log($"✅ AAR built: {aarPath}");
|
||||
|
||||
// 生成压缩 JSON (key 只保留文件名)
|
||||
Dictionary<string, string> simpleMapping = new Dictionary<string, string>();
|
||||
foreach (var kv in mapping)
|
||||
{
|
||||
string fileName = Path.GetFileName(kv.Key);
|
||||
simpleMapping[fileName] = kv.Value;
|
||||
}
|
||||
|
||||
string mappingJson = GenerateMappingJson(mapping);
|
||||
// 更新 mappingInfo
|
||||
UpdateMappingInEFSdk_LineByLine(mappingJson);
|
||||
// 映射文件
|
||||
string mappingPath = Path.Combine(TempDir, "res_mapping.json");
|
||||
File.WriteAllText(mappingPath, mappingJson);
|
||||
|
||||
// 清理临时目录
|
||||
Directory.Delete(TempDir, true);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private static void CopyAndRenameFiles(string srcDir, string dstDir, out Dictionary<string, string> mapping)
|
||||
{
|
||||
mapping = new Dictionary<string, string>();
|
||||
foreach (var filePath in Directory.GetFiles(srcDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (filePath.EndsWith(".meta")) continue;
|
||||
|
||||
string relativePath = filePath.Substring(srcDir.Length + 1).Replace("\\", "/");
|
||||
string newName = GenerateRandomAndroidName(Path.GetExtension(filePath));
|
||||
mapping[Path.GetFileName(filePath)] = newName;
|
||||
|
||||
string dstPath = Path.Combine(dstDir, newName);
|
||||
string dstFolder = Path.GetDirectoryName(dstPath);
|
||||
if (!Directory.Exists(dstFolder)) Directory.CreateDirectory(dstFolder);
|
||||
|
||||
File.Copy(filePath, dstPath);
|
||||
}
|
||||
|
||||
foreach (var dir in Directory.GetDirectories(srcDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
string relativeDir = dir.Substring(srcDir.Length + 1);
|
||||
string dstSubDir = Path.Combine(dstDir, relativeDir);
|
||||
if (!Directory.Exists(dstSubDir)) Directory.CreateDirectory(dstSubDir);
|
||||
}
|
||||
|
||||
Debug.Log("✅ Files copied and renamed");
|
||||
}
|
||||
private static string GenerateMappingJson(Dictionary<string, string> mapping)
|
||||
{
|
||||
var items = new List<MappingItem>();
|
||||
foreach (var kv in mapping)
|
||||
{
|
||||
items.Add(new MappingItem { key = kv.Key, value = kv.Value });
|
||||
}
|
||||
MappingListWrapper wrapper = new MappingListWrapper { items = items };
|
||||
return JsonUtility.ToJson(wrapper, false);
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
private class MappingItem { public string key; public string value; }
|
||||
|
||||
[System.Serializable]
|
||||
private class MappingListWrapper { public List<MappingItem> items; }
|
||||
private static string GenerateRandomAndroidName(string ext)
|
||||
{
|
||||
int len = UnityEngine.Random.Range(6, 12);
|
||||
string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
string name = "";
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
name += chars[UnityEngine.Random.Range(0, chars.Length)];
|
||||
}
|
||||
|
||||
if (!char.IsLetter(name[0])) name = "a" + name.Substring(1);
|
||||
|
||||
return name + ext;
|
||||
}
|
||||
|
||||
private static void UpdateMappingInEFSdk_LineByLine(string mappingJson)
|
||||
{
|
||||
if (!File.Exists(EFSdk_FILE))
|
||||
{
|
||||
Debug.LogError($"EFSdk.cs not found: {EFSdk_FILE}");
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(EFSdk_FILE);
|
||||
bool updated = false;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i].Contains("mappingInfo"))
|
||||
{
|
||||
lines[i] = $" private static string mappingInfo = @\"{mappingJson.Replace("\"", "\"\"")}\";";
|
||||
updated = true;
|
||||
break; // 找到第一行就替换,防止重复
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
// 如果没有找到 mappingInfo 行,则在 _mEfSdk 后插入
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i].Contains("private static EFSdk _mEfSdk"))
|
||||
{
|
||||
lines[i] += $"\n private static string mappingInfo = @\"{mappingJson.Replace("\"", "\"\"")}\";";
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(EFSdk_FILE, lines);
|
||||
Debug.Log("✅ mappingInfo updated in EFSdk.cs (line-by-line)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ff87df6ca3d9445e98929bb62fb10a15
|
||||
timeCreated: 1756694090
|
|
@ -0,0 +1,13 @@
|
|||
<dependencies>
|
||||
<androidPackages>
|
||||
<androidPackage spec="com.earn.money:sdk:+">
|
||||
<repositories>
|
||||
<repository>https://repo.dgtverse.cn/repository/maven-public/</repository>
|
||||
<repository>https://android-sdk.is.com/</repository>
|
||||
<repository>https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea</repository>
|
||||
<repository>https://artifact.bytedance.com/repository/pangle/</repository>
|
||||
</repositories>
|
||||
</androidPackage>
|
||||
</androidPackages>
|
||||
|
||||
</dependencies>
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 61484455ad49448f839552619a42625a
|
||||
timeCreated: 1756628147
|
|
@ -0,0 +1,181 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Unity.Plastic.Newtonsoft.Json.Linq;
|
||||
using UnityEditor.Android;
|
||||
using UnityEngine;
|
||||
|
||||
public class DynamicApplicationClass : IPostGenerateGradleAndroidProject
|
||||
{
|
||||
public int callbackOrder
|
||||
{
|
||||
get { return int.MaxValue; }
|
||||
}
|
||||
|
||||
private string GetRootPath(string path)
|
||||
{
|
||||
return Path.Combine(path, "..", "");
|
||||
}
|
||||
|
||||
public void OnPostGenerateGradleAndroidProject(string path)
|
||||
{
|
||||
var androidManifest = new SDKTool.AndroidManifest(SDKTool.GetManifestPath(path));
|
||||
androidManifest.SetStartingActivityAttribute("hardwareAccelerated", "true");
|
||||
androidManifest.Save();
|
||||
SetGradleConstraints(path);
|
||||
FixedAddressValueTypeAttribute(path);
|
||||
ParseConfigFile(path);
|
||||
}
|
||||
|
||||
private static void SetGradleConstraints(string path)
|
||||
{
|
||||
string gradlePath = Path.Combine(path, "../unityLibrary", "build.gradle");
|
||||
|
||||
if (!File.Exists(gradlePath))
|
||||
{
|
||||
Debug.LogError("未找到unityLibrary模块的build.gradle文件: " + gradlePath);
|
||||
}
|
||||
|
||||
// var content = " implementation \"androidx.datastore:datastore:1.0.0\"\n constraints {\n implementation(\"androidx.datastore:datastore\") {\n version {\n strictly \"1.0.0\"\n }\n because \"1.0.0版本,避免高版本兼容性问题\"\n }\n }";
|
||||
var buildGradleOutLines = new List<string>();
|
||||
foreach (var line in File.ReadLines(gradlePath))
|
||||
{
|
||||
if (line.Trim().Contains("com.earn.money:sdk"))
|
||||
{
|
||||
Debug.Log("找到com.earn.money:sdk");
|
||||
buildGradleOutLines.Add($" implementation ('com.earn.money:sdk:{SDKTool.GetSDKVersion()}')");
|
||||
}
|
||||
else
|
||||
{
|
||||
buildGradleOutLines.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(gradlePath, string.Join("\n", buildGradleOutLines.ToArray()) + "\n");
|
||||
}
|
||||
|
||||
|
||||
private static void FixedAddressValueTypeAttribute(string path)
|
||||
{
|
||||
string launcherBuildGradlePath = Path.Combine(path, "../launcher", "build.gradle");
|
||||
string content =
|
||||
" configurations.all {\n resolutionStrategy {\n force \"androidx.appcompat:appcompat:1.6.1\"\n force \"androidx.core:core:1.12.0\"\n }\n }";
|
||||
var launcherBuildGradleOutLines = new List<string>();
|
||||
foreach (var line in File.ReadLines(launcherBuildGradlePath))
|
||||
{
|
||||
launcherBuildGradleOutLines.Add(line);
|
||||
if (line.Trim().StartsWith("defaultConfig"))
|
||||
{
|
||||
launcherBuildGradleOutLines.Add(content);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(launcherBuildGradlePath, string.Join("\n", launcherBuildGradleOutLines.ToArray()) + "\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ParseConfigFile(string path)
|
||||
{
|
||||
Dictionary<string, string> jsonDict = ParseToSimpleDictionary();
|
||||
if (jsonDict == null || jsonDict.Count == 0)
|
||||
{
|
||||
throw new System.Exception("配置文件中未解析到google-services.json");
|
||||
}
|
||||
|
||||
// 获取launcher模块的build.gradle路径
|
||||
string gradlePath = Path.Combine(path, "../launcher", "build.gradle");
|
||||
|
||||
if (!File.Exists(gradlePath))
|
||||
{
|
||||
Debug.LogError("未找到launcher模块的build.gradle文件: " + gradlePath);
|
||||
}
|
||||
// 读取文件内容
|
||||
string newResValue = $"\n resValue \"string\", \"game_services_project_id\", \"{jsonDict["project_id"]}\"" +
|
||||
$"\n resValue \"string\", \"project_id\", \"{jsonDict["project_id"]}\"" +
|
||||
$"\n resValue \"string\", \"google_project_id\", \"{jsonDict["project_id"]}\"" +
|
||||
$"\n resValue \"string\", \"google_package_name\", \"{jsonDict["package_name"]}\"" +
|
||||
$"\n resValue \"string\", \"gcm_defaultSenderId\", \"{jsonDict["project_number"]}\"" +
|
||||
$"\n resValue \"string\", \"google_api_key\", \"{jsonDict["current_key"]}\"" +
|
||||
$"\n resValue \"string\", \"google_app_id\", \"{jsonDict["mobilesdk_app_id"]}\"" +
|
||||
$"\n resValue \"string\", \"google_crash_reporting_api_key\", \"{jsonDict["current_key"]}\"" +
|
||||
$"\n resValue \"string\", \"google_storage_bucket\", \"{jsonDict["storage_bucket"]}\"";
|
||||
Debug.Log($"DSSdk newResValue: {newResValue}");
|
||||
string launcherBuildGradlePath = Path.Combine(path, "../launcher", "build.gradle");
|
||||
var launcherBuildGradleOutLines = new List<string>();
|
||||
foreach (var line in File.ReadLines(launcherBuildGradlePath))
|
||||
{
|
||||
launcherBuildGradleOutLines.Add(line);
|
||||
if (line.Trim().StartsWith("defaultConfig"))
|
||||
{
|
||||
launcherBuildGradleOutLines.Add(newResValue);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(launcherBuildGradlePath, string.Join("\n", launcherBuildGradleOutLines.ToArray()) + "\n");
|
||||
|
||||
}
|
||||
|
||||
public Dictionary<string, string> ParseToSimpleDictionary()
|
||||
{
|
||||
Dictionary<string, string> simpleDict = new Dictionary<string, string>();
|
||||
|
||||
try
|
||||
{
|
||||
string filePath = Path.Combine(Application.streamingAssetsPath, "google-services.json");
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
Debug.LogError("文件不存在: " + filePath);
|
||||
return simpleDict;
|
||||
}
|
||||
|
||||
string jsonContent = File.ReadAllText(filePath);
|
||||
JObject jsonObject = JObject.Parse(jsonContent);
|
||||
|
||||
// 递归解析所有字段
|
||||
ParseJToken(jsonObject, simpleDict);
|
||||
return simpleDict;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError("解析错误: " + e.Message);
|
||||
return simpleDict;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归解析JToken并提取字段名和值
|
||||
/// </summary>
|
||||
private void ParseJToken(JToken token, Dictionary<string, string> dict)
|
||||
{
|
||||
if (token is JObject jObject)
|
||||
{
|
||||
foreach (var property in jObject.Properties())
|
||||
{
|
||||
// 如果是对象类型,继续递归解析
|
||||
if (property.Value is JObject || property.Value is JArray)
|
||||
{
|
||||
ParseJToken(property.Value, dict);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 基本类型直接添加,相同key会覆盖
|
||||
if (dict.ContainsKey(property.Name))
|
||||
{
|
||||
Debug.LogWarning($"字段名重复,已覆盖: {property.Name}");
|
||||
}
|
||||
dict[property.Name] = property.Value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token is JArray jArray)
|
||||
{
|
||||
// 解析数组中的每个元素
|
||||
foreach (var item in jArray)
|
||||
{
|
||||
ParseJToken(item, dict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e118bf217da84e56846255d208995b6c
|
||||
timeCreated: 1756628731
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
public class SDKEditorNetworkTool
|
||||
{
|
||||
public static string GetText(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (UnityWebRequest www = UnityWebRequest.Get(url))
|
||||
{
|
||||
www.timeout = 10; // 设置超时时间为10秒
|
||||
www.SendWebRequest(); // 同步发送请求
|
||||
|
||||
while (www.isDone == false)
|
||||
{
|
||||
}
|
||||
|
||||
if (www.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
Debug.LogError($"Request failed: {www.error}");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
if (www.responseCode != 200)
|
||||
{
|
||||
Debug.LogWarning($"Unexpected status code: {www.responseCode}");
|
||||
return "";
|
||||
}
|
||||
|
||||
return www.downloadHandler.text;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 69514595cf674f94bd3e0b15beb30d73
|
||||
timeCreated: 1756628731
|
|
@ -0,0 +1,300 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.CSharp.RuntimeBinder;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEngine;
|
||||
|
||||
public class SDKTool
|
||||
{
|
||||
public static string GetPackageName()
|
||||
{
|
||||
return PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
|
||||
}
|
||||
|
||||
public static string MavenStr = @"
|
||||
maven {
|
||||
url 'https://repo.dgtverse.cn/repository/maven-public/'
|
||||
name 'maven_repo.dgtverse.cn'
|
||||
}
|
||||
|
||||
";
|
||||
public static Hashtable LoadProperties(string file)
|
||||
{
|
||||
var ht = new Hashtable(16);
|
||||
string content = null;
|
||||
try
|
||||
{
|
||||
content = file;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rows = content.Split('\n');
|
||||
string[] kv = null;
|
||||
foreach (var c in rows)
|
||||
{
|
||||
if (c.Trim().Length == 0)
|
||||
continue;
|
||||
kv = c.Split('=');
|
||||
ht[kv[0].Trim()] = kv.Length switch
|
||||
{
|
||||
1 => "",
|
||||
2 => kv[1].Trim(),
|
||||
_ => ht[kv[0].Trim()]
|
||||
};
|
||||
}
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
public static string GetSDKVersion()
|
||||
{
|
||||
var xmlText =
|
||||
SDKEditorNetworkTool.GetText("https://repo.dgtverse.cn/repository/tk_my/com/earn/money/sdk/maven-metadata.xml");
|
||||
if (string.IsNullOrEmpty(xmlText))
|
||||
{
|
||||
throw new RuntimeBinderException(
|
||||
"获取版本号失败 , 接口请求返回为空,或请求不到. https://repo.dgtverse.cn/repository/tk_my/com/earn/money/sdk/maven-metadata.xml");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(xmlText);
|
||||
|
||||
XmlNodeList versions = xmlDoc.SelectNodes("//versioning/latest");
|
||||
if (versions == null)
|
||||
{
|
||||
throw new RuntimeBinderException("获取版本号失败");
|
||||
}
|
||||
|
||||
if (versions.Count > 0)
|
||||
{
|
||||
string latestVersion = versions[0].InnerText;
|
||||
Debug.Log($"Latest version: {latestVersion}");
|
||||
return latestVersion;
|
||||
}
|
||||
|
||||
throw new RuntimeBinderException("解析xml失败");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeBinderException($"获取版本号失败 : XML parsing error: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CopyGoogleServices(string path)
|
||||
{
|
||||
// var gpPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "SDKConfig" +
|
||||
// Path.DirectorySeparatorChar + SDKTool.GetPackageName() + Path.DirectorySeparatorChar +
|
||||
// "google-services.json";
|
||||
string gpPath = Path.Combine(Application.streamingAssetsPath, "google-services.json");
|
||||
if (!File.Exists(gpPath))
|
||||
{
|
||||
throw new BuildFailedException("Can't find google-services.json");
|
||||
}
|
||||
|
||||
var targetPath = path + Path.DirectorySeparatorChar + ".." + Path.DirectorySeparatorChar + "launcher" +
|
||||
Path.DirectorySeparatorChar + "google-services.json";
|
||||
Debug.Log("gpPath : " + gpPath + "\t targetPath : " + targetPath);
|
||||
if (File.Exists(targetPath))
|
||||
{
|
||||
File.Delete(targetPath);
|
||||
}
|
||||
|
||||
File.Copy(gpPath, targetPath);
|
||||
}
|
||||
|
||||
private void AddFirebasePluginToGradle(string path)
|
||||
{
|
||||
var filePath =
|
||||
$"{path}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}launcher{Path.DirectorySeparatorChar}build.gradle";
|
||||
if (File.ReadAllText(filePath).Contains("com.google.gms.google-services"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newLine = new List<string>
|
||||
{
|
||||
"apply plugin: 'com.google.gms.google-services'\n",
|
||||
"apply plugin: 'com.google.firebase.crashlytics'\n"
|
||||
};
|
||||
newLine.AddRange(File.ReadLines(filePath));
|
||||
File.WriteAllLines(filePath, newLine);
|
||||
}
|
||||
|
||||
private void AddFirebasePlugin(string rootPath)
|
||||
{
|
||||
_AddFirebasePlugin($"{rootPath}{Path.DirectorySeparatorChar}build.gradle");
|
||||
}
|
||||
|
||||
private void _AddFirebasePlugin(string filePath)
|
||||
{
|
||||
if (File.ReadAllText(filePath).Contains("com.google.gms:google-services"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newLine = new List<string>();
|
||||
foreach (var line in File.ReadLines(filePath))
|
||||
{
|
||||
var trim = line.Trim();
|
||||
if (trim.Contains("com.google.gms.google-services") && trim.Contains("4.4.2"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trim.Contains("com.google.firebase.crashlytics") && trim.Contains("2.9.4"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trim.Contains("com.google.gms:google-services") && trim.Contains("4.3.10"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trim.Contains("com.google.firebase:firebase-crashlytics-gradle") && trim.Contains("2.9.4"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
newLine.Add(line);
|
||||
if (trim.StartsWith("google()"))
|
||||
{
|
||||
newLine.Add(MavenStr);
|
||||
}
|
||||
|
||||
if (trim.StartsWith("classpath"))
|
||||
{
|
||||
newLine.Add("classpath 'com.google.gms:google-services:4.3.10'\n");
|
||||
newLine.Add("classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4'\n");
|
||||
}
|
||||
|
||||
if (trim.StartsWith("id 'com.android.application'"))
|
||||
{
|
||||
newLine.Add("id 'com.google.gms.google-services' version '4.4.2' apply false\n");
|
||||
newLine.Add("id 'com.google.firebase.crashlytics' version '2.9.4' apply false\n");
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(filePath, newLine);
|
||||
}
|
||||
|
||||
private static string _manifestFilePath;
|
||||
|
||||
public static string GetManifestPath(string basePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_manifestFilePath))
|
||||
{
|
||||
StringBuilder pathBuilder = new StringBuilder(basePath);
|
||||
pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
|
||||
pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
|
||||
pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml");
|
||||
_manifestFilePath = pathBuilder.ToString();
|
||||
Debug.Log($"_manifestFilePath = {_manifestFilePath}");
|
||||
|
||||
}
|
||||
|
||||
return _manifestFilePath;
|
||||
}
|
||||
internal class AndroidXmlDocument : XmlDocument
|
||||
{
|
||||
private string m_Path;
|
||||
protected XmlNamespaceManager nsMgr;
|
||||
public const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
|
||||
|
||||
public AndroidXmlDocument(string path)
|
||||
{
|
||||
m_Path = path;
|
||||
using (var reader = new XmlTextReader(m_Path))
|
||||
{
|
||||
reader.Read();
|
||||
Load(reader);
|
||||
}
|
||||
|
||||
nsMgr = new XmlNamespaceManager(NameTable);
|
||||
nsMgr.AddNamespace("android", AndroidXmlNamespace);
|
||||
}
|
||||
|
||||
public string Save()
|
||||
{
|
||||
return SaveAs(m_Path);
|
||||
}
|
||||
|
||||
public string SaveAs(string path)
|
||||
{
|
||||
using (var writer = new XmlTextWriter(path, new UTF8Encoding(false)))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
Save(writer);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
internal class AndroidManifest : AndroidXmlDocument
|
||||
{
|
||||
private readonly XmlElement ApplicationElement;
|
||||
|
||||
public AndroidManifest(string path) : base(path)
|
||||
{
|
||||
ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
|
||||
}
|
||||
|
||||
private XmlAttribute CreateAndroidAttribute(string key, string value, string prefix = "android", string namespaceURI = AndroidXmlNamespace)
|
||||
{
|
||||
XmlAttribute attr = CreateAttribute(prefix, key, namespaceURI);
|
||||
attr.Value = value;
|
||||
return attr;
|
||||
}
|
||||
|
||||
internal XmlNode GetActivityWithLaunchIntent()
|
||||
{
|
||||
return SelectSingleNode(
|
||||
"/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " +
|
||||
"intent-filter/category/@android:name='android.intent.category.LAUNCHER']", nsMgr);
|
||||
}
|
||||
|
||||
internal void SetApplicationTheme(string appTheme)
|
||||
{
|
||||
ApplicationElement.Attributes.Append(CreateAndroidAttribute("theme", appTheme));
|
||||
}
|
||||
|
||||
internal void SetStartingActivityName(string activityName)
|
||||
{
|
||||
GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("name", activityName));
|
||||
}
|
||||
|
||||
internal void SetApplicationAttribute(string key, string value, string prefix = "android", string namespaceURI = AndroidXmlNamespace)
|
||||
{
|
||||
ApplicationElement.Attributes.Append(CreateAndroidAttribute(key, value, prefix, namespaceURI));
|
||||
}
|
||||
|
||||
internal void RemoveApplicationAttribute(string key)
|
||||
{
|
||||
// ApplicationElement.Attributes.Remove(CreateAndroidAttribute(key, key));
|
||||
var removeNamedItem = ApplicationElement.Attributes.RemoveNamedItem(key);
|
||||
Debug.Log($"删除节点 key = {key} value = {removeNamedItem}");
|
||||
}
|
||||
|
||||
internal void SetStartingActivityAttribute(string key, string value)
|
||||
{
|
||||
XmlNode node = GetActivityWithLaunchIntent();
|
||||
Debug.Log($"Main节点 node = {node} key = {key} value = {value}");
|
||||
if (node != null)
|
||||
{
|
||||
XmlAttributeCollection attributes = node.Attributes;
|
||||
attributes.Append(CreateAndroidAttribute(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 01a2130a27404acabf67d5122908d668
|
||||
timeCreated: 1756628731
|
|
@ -0,0 +1,175 @@
|
|||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
|
||||
namespace EFSDK
|
||||
{
|
||||
public class WLoom : MonoBehaviour
|
||||
{
|
||||
public static int maxThreads = 8;
|
||||
static int numThreads;
|
||||
|
||||
private static WLoom _current;
|
||||
|
||||
//private int _count;
|
||||
public static WLoom Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Initialize();
|
||||
return _current;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_current = this;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
static bool initialized;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
initialized = true;
|
||||
var g = new GameObject("Loom");
|
||||
_current = g.AddComponent<WLoom>();
|
||||
DontDestroyOnLoad(g);
|
||||
}
|
||||
}
|
||||
|
||||
public struct NoDelayedQueueItem
|
||||
{
|
||||
public Action<object> action;
|
||||
public object param;
|
||||
}
|
||||
|
||||
private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
|
||||
|
||||
public struct DelayedQueueItem
|
||||
{
|
||||
public float time;
|
||||
public Action<object> action;
|
||||
public object param;
|
||||
}
|
||||
|
||||
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
|
||||
|
||||
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
|
||||
|
||||
public static void QueueOnMainThread(Action<object> taction, object tparam)
|
||||
{
|
||||
QueueOnMainThread(taction, tparam, 0f);
|
||||
}
|
||||
|
||||
public static void QueueOnMainThread(Action<object> taction, object tparam, float time)
|
||||
{
|
||||
if (time != 0)
|
||||
{
|
||||
lock (Current._delayed)
|
||||
{
|
||||
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (Current._actions)
|
||||
{
|
||||
Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Thread RunAsync(Action a)
|
||||
{
|
||||
Initialize();
|
||||
while (numThreads >= maxThreads)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref numThreads);
|
||||
ThreadPool.QueueUserWorkItem(RunAction, a);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void RunAction(object action)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Action)action)();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref numThreads);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (_current == this)
|
||||
{
|
||||
_current = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ToukaGamesInit()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Use this for initialization
|
||||
void Start()
|
||||
{
|
||||
}
|
||||
|
||||
List<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>();
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
if (_actions.Count > 0)
|
||||
{
|
||||
lock (_actions)
|
||||
{
|
||||
_currentActions.Clear();
|
||||
_currentActions.AddRange(_actions);
|
||||
_actions.Clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < _currentActions.Count; i++)
|
||||
{
|
||||
_currentActions[i].action(_currentActions[i].param);
|
||||
}
|
||||
}
|
||||
|
||||
if (_delayed.Count > 0)
|
||||
{
|
||||
lock (_delayed)
|
||||
{
|
||||
_currentDelayed.Clear();
|
||||
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
|
||||
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||
{
|
||||
_delayed.Remove(_currentDelayed[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||
{
|
||||
_currentDelayed[i].action(_currentDelayed[i].param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3aeb3cde9e8947319795ca9d78758647
|
||||
timeCreated: 1745562486
|
Loading…
Reference in New Issue