Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sathyarajshetigar/da96fb2701882980c1f7f5434e8dd6bb to your computer and use it in GitHub Desktop.
Save sathyarajshetigar/da96fb2701882980c1f7f5434e8dd6bb to your computer and use it in GitHub Desktop.
#if USE_PUSH
using OneSignalSDK.User.Models;
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
using DG.Tweening;
using IjsUiFramework;
using OneSignalSDK;
using OneSignalSDK.Debug.Models;
using OneSignalSDK.InAppMessages;
using OneSignalSDK.Notifications;
using OneSignalSDK.Notifications.Models;
using OneSignalSDK.User;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.SceneManagement;
using Utils;
using static Msc.Facade;
//unity 6 android build issue solution
// https://github.com/OneSignal/OneSignal-Unity-SDK/issues/752
namespace Managers
{
public class OneSignalController : MonoBehaviour
{
#region Logger
private readonly IJSLogger _logger = new("OneSignalController", Color.yellow);
[SerializeField] [BoxGroup("Logs")] private bool _enableLogs;
[SerializeField] [BoxGroup("Logs")] private Color _logColor;
private void OnValidate()
{
_logger.ToggleLogs(_enableLogs);
_logger.ModifyColor(_logColor);
}
#endregion
private string _appID, _apiAuthKey;
protected OneSignalController()
{
}
public static OneSignalController instance;
void OnEnable()
{
PlayFabManager.onPlayfabSetupCompleteEvent += onPlayfabSetupComplete;
}
void OnDisable()
{
PlayFabManager.onPlayfabSetupCompleteEvent -= onPlayfabSetupComplete;
}
void Awake()
{
_logger.ToggleLogs(_enableLogs);
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
}
}
private void Start()
{
Init();
}
private void onPlayfabSetupComplete(bool success)
{
Game.Log($"onPlayfabSetupComplete {success}");
#if UNITY_IOS && !UNITY_EDITOR
if (PlayerPreference.launchCount >= 2)//don't show on first launch. there is a high chance the player will not allow the notification
Init();
#else
Init();
#endif
}
private void Init()
{
#if USE_PUSH
// Enable the lines below to debug issues with OneSignal
OneSignal.Debug.LogLevel = LogLevel.Verbose;
OneSignal.Debug.AlertLevel = LogLevel.Fatal;
_appID = gameData.oneSignalID;
_apiAuthKey = gameData.oneSignalAPIKey;
_logger.PrintLog($"Initializing with appId <b>{_appID}</b>");
OneSignal.Initialize(_appID);
// Set up the default live activity
OneSignal.LiveActivities.SetupDefault();
// Setting ConsentRequired to true will prevent the OneSignalSDK from operating until
// PrivacyConsent is also set to true
#if UNITY_ANDROID
OneSignal.ConsentRequired = false;
#else
_logger.PrintLog($"Prompt For Push");
if (PlayerPrefs.GetInt("dontAskAgain", 0) == 1)
_logger.PrintLog($"Dont Ask Again");
else if (PlayerPrefs.GetInt("gavePermission", 0) == 1)
_logger.PrintLog($"gavePermission");
else
{
OneSignal.ConsentGiven = true;
OneSignal.ConsentRequired = false;
_logger.PrintLog($"ConsentRequired");
OneSignal.User.PushSubscription.OptIn();
}
#endif
// Set up the below to listen for and respond to events from notifications
OneSignal.Notifications.Clicked += _notificationOnClick;
OneSignal.Notifications.ForegroundWillDisplay += _notificationOnDisplay;
OneSignal.Notifications.PermissionChanged += _notificationPermissionChanged;
// Set up the below to listen for and respond to events from in-application messages
OneSignal.InAppMessages.WillDisplay += _iamWillDisplay;
OneSignal.InAppMessages.DidDisplay += _iamDidDisplay;
OneSignal.InAppMessages.WillDismiss += _iamWillDismiss;
OneSignal.InAppMessages.DidDismiss += _iamDidDismiss;
OneSignal.InAppMessages.Clicked += _iamOnClick;
// Set up the below to listen for and respond to state changes
OneSignal.User.PushSubscription.Changed += _pushSubscriptionChanged;
OneSignal.User.Changed += _userStateChanged;
var myOneSignalID = OneSignal.User.PushSubscription.Id;
if (!string.IsNullOrEmpty(myOneSignalID))
{
if (!myOneSignalID.Equals(PlayerPreference.oneSignalID))
PlayerPreference.oneSignalID = myOneSignalID;
PlayFabManager.instance.UpdateUserData(Game.PTD_PUSHID, myOneSignalID, false);
}
else
Game.Log("OneSignal PushNotificationManager onPlayfabSetupComplete localUserID is null................................................");
#endif
}
#if USE_PUSH
private void _notificationOnClick(object sender, NotificationClickEventArgs e)
{
OnNotificationOpen(e.Notification);
}
private void OnNotificationOpen(INotification notification)
{
_logger.PrintLog($"Notification was clicked with Notification: {JsonUtility.ToJson(notification)}");
var additionalData = (Dictionary<string, object>)notification.AdditionalData;
foreach (var item in additionalData)
_logger.PrintLog($"item Key {item.Key} Value {item.Value}");
if (additionalData.TryGetValue("type", out var typeObj))
{
var type = typeObj.ToString();
switch (type)
{
case "Invitation":
{
if (additionalData.TryGetValue("roomCode", out var roomCodeObj)
&& additionalData.TryGetValue("playerName", out var playerNameObj)
&& additionalData.TryGetValue("senderPushID", out var senderPushIDObj))
{
_logger.PrintLog($"roomCodeObj: {roomCodeObj == null} playerNameObj {playerNameObj == null}");
if (roomCodeObj != null && playerNameObj != null && senderPushIDObj != null)
{
var roomCode = roomCodeObj.ToString();
var playerName = playerNameObj.ToString();
var senderPushID = senderPushIDObj.ToString();
_logger.PrintLog($"RoomCode: {roomCode}");
_logger.PrintLog($"PlayerName: {playerName}");
_logger.PrintLog($"senderPushID: {senderPushID}");
if (!string.IsNullOrEmpty(roomCode))
{
HandleNotificationResponse(playerName, roomCode, senderPushID);
}
else
{
Game.acceptingInvite = false;
Game.roomName = string.Empty;
}
}
}
break;
}
case "InvitationResponse":
{
if (additionalData.TryGetValue("response", out var responseObj))
{
var response = responseObj.ToString();
_logger.PrintLog($"InvitationResponse received: {response}");
// Get additional data for better context
string roomCode = "";
string playerName = "";
if (additionalData.TryGetValue("roomCode", out var roomCodeObj))
roomCode = roomCodeObj.ToString();
if (additionalData.TryGetValue("playerName", out var playerNameObj))
playerName = playerNameObj.ToString();
_logger.PrintLog($"InvitationResponse details - roomCode: {roomCode}, playerName: {playerName}");
// Only show toast if the sender is still in a game room
// If roomCode is provided, check if sender is in that specific room
bool shouldShowToast = false;
if (photonManager?.inRoom ?? false)
{
if (!string.IsNullOrEmpty(roomCode))
{
// Check if sender is in the same room that was invited to
shouldShowToast = Game.roomName.Equals(roomCode, StringComparison.OrdinalIgnoreCase);
_logger.PrintLog($"Room validation - Current room: {Game.roomName}, Invited room: {roomCode}, Match: {shouldShowToast}");
}
else
{
// Fallback: show toast if sender is in any room
shouldShowToast = true;
_logger.PrintLog("No roomCode provided, showing toast since sender is in a room");
}
}
if (shouldShowToast)
{
string toastMessage;
if (response.Contains("Accepted"))
{
toastMessage = $"{playerName} {Game.GetLocTxt(LKeys.ACCEPTEDREQUEST)}";
}
else if (response.Contains("Rejected"))
{
toastMessage = $"{playerName} {Game.GetLocTxt(LKeys.REJREQ)}";
}
else
{
// Fallback: use the full message from notification
toastMessage = notification.Body ?? response;
}
_logger.PrintLog($"Showing invitation response toast: {toastMessage}");
DialogBox.ShowToastMessage(toastMessage, 3f);
}
else
{
_logger.PrintLog("InvitationResponse received but sender is not in the invited game room - toast not shown");
}
}
break;
}
}
}
ClearPush();
}
private void HandleNotificationResponse(string playerName, string roomCode, string senderPushID)
{
DialogBox.ShowReplacingDialog(Game.GetLocTxt(LKeys.PRIVATEGAME), $"{playerName} {Game.GetLocTxt(LKeys.SENTUAREQUEST)}",
Game.GetLocTxt(LKeys.JOIN), Game.GetLocTxt(LKeys.CANCEL), (join, didcancel) =>
{
if (join)
{
DialogBox.ShowToastMessage($"{Game.GetLocTxt(LKeys.ROOMID)} {roomCode}", 3f);
Game.SetGameMode(PlayModes.OnlinePrivate);
Game.acceptingInvite = true;
Game.roomName = roomCode;
// Fix for Bug 143: Set isNewGame to true when accepting invites from game over screen
// This ensures proper scene initialization and blackout panel management
if (Game.uiScreen >= UIScreens.PauseScreen)
{
Game.Log($"HandleNotification: Setting isNewGame=true for invite from game screen. uiScreen={Game.uiScreen}");
Game.isNewGame = true;
}
Game.Log("HandleNotification roomName : " + roomCode + " uiScreen " + Game.uiScreen + " inroom? " +
photonManager?.inRoom);
if (Game.uiScreen >= UIScreens.PauseScreen)
{
if (photonManager?.inRoom ?? false)
photonManager?.Disconnect(ReloadGameSceneAndJoinTheGame);
else
ReloadGameSceneAndJoinTheGame();
}
else if (Game.uiScreen < UIScreens.PauseScreen)
{
gameManager.introScreenUIController?.PlayOnlinePrivate();
}
// SendResponse(senderPushID, $"{PlayerProfile.playerName} {Game.GetLocTxt(LKeys.ACCEPTEDREQUEST)}");
// SendResponse(senderPushID, $"{PlayerProfile.playerName} Accepted your request.");
SendInviteResponseAsync(senderPushID, $"{PlayerProfile.playerName} {Game.GetLocTxt(LKeys.ACCEPTEDREQUEST)}", "Accepted", roomCode,
PlayerProfile.playerName);
//SendResponse(senderPushID, "Accepted", "");
}
else
{
Game.acceptingInvite = false;
Game.roomName = string.Empty;
// SendResponse(senderPushID, $"{PlayerProfile.playerName} {Game.GetLocTxt(LKeys.REJREQ)}");
// SendResponse(senderPushID, $"{PlayerProfile.playerName} Rejected your request.");
SendInviteResponseAsync(senderPushID, $"{PlayerProfile.playerName} {Game.GetLocTxt(LKeys.REJREQ)}", "Rejected", roomCode,
PlayerProfile.playerName);
}
});
}
private void _notificationOnDisplay(object sender, NotificationWillDisplayEventArgs e)
{
var additionalData = e.Notification.AdditionalData != null
? Json.Serialize(e.Notification.AdditionalData)
: null;
_logger.PrintLog($"Notification was received in foreground: {JsonUtility.ToJson(e.Notification)}\n{additionalData}");
e.Notification.Display();
OnNotificationOpen(e.Notification);
}
private void _notificationPermissionChanged(object sender, NotificationPermissionChangedEventArgs e)
{
_logger.PrintLog($"Notification Permission changed to: {e.Permission}");
}
private void _iamWillDisplay(object sender, InAppMessageWillDisplayEventArgs e)
{
_logger.PrintLog($"IAM will display: {JsonUtility.ToJson(e.Message)}");
}
private void _iamDidDisplay(object sender, InAppMessageDidDisplayEventArgs e)
{
_logger.PrintLog($"IAM did display: {JsonUtility.ToJson(e.Message)}");
}
private void _iamWillDismiss(object sender, InAppMessageWillDismissEventArgs e)
{
_logger.PrintLog($"IAM will dismiss: {JsonUtility.ToJson(e.Message)}");
}
private void _iamDidDismiss(object sender, InAppMessageDidDismissEventArgs e)
{
_logger.PrintLog($"IAM did dismiss: {JsonUtility.ToJson(e.Message)}");
}
private void _iamOnClick(object sender, InAppMessageClickEventArgs e)
{
_logger.PrintLog($"IAM was clicked with Message: {JsonUtility.ToJson(e.Message)}");
_logger.PrintLog($"IAM was clicked with Result: {JsonUtility.ToJson(e.Result)}");
_logger.PrintLog($"IAM was clicked with Result UrlTarget: " + e.Result.UrlTarget);
}
private void _pushSubscriptionChanged(object sender, PushSubscriptionChangedEventArgs e)
{
_logger.PrintLog($"Push subscription changed from previous: {JsonUtility.ToJson(e.State.Previous)}");
_logger.PrintLog($"Push subscription changed to current: {JsonUtility.ToJson(e.State.Current)}");
//TODO:Store it somewhere
//Called only once during the first launch
var idToStore = e.State.Current.Id;
_logger.PrintLog($"_pushSubscriptionChanged idToStore : {idToStore}");
PlayerPreference.oneSignalID = idToStore;
}
private void _userStateChanged(object sender, UserStateChangedEventArgs e)
{
_logger.PrintLog($"OneSignalId changed : {e.State.Current.OneSignalId}");
_logger.PrintLog($"ExternalId changed : {e.State.Current.ExternalId}");
if (!string.IsNullOrEmpty(e.State.Current.OneSignalId))
_logger.PrintLog("OneSignalIdReceived: " + e.State.Current.OneSignalId);
else
_logger.PrintLog("PushNotificationManager localUserID is null");
}
private void ClearPush()
{
_logger.PrintLog("Clearing existing OneSignal push notifications");
OneSignal.Notifications.ClearAllNotifications();
_logger.PrintLog("Notifications cleared");
}
#endif
public async Task<bool> InviteAsync(string toUserID, string message, string roomCode, string playerName, string senderPushID)
{
try
{
#if USE_PUSH
// Validate configuration
if (string.IsNullOrEmpty(_appID) || string.IsNullOrEmpty(_apiAuthKey))
{
_logger.PrintLog("OneSignal configuration is missing. App ID or API Key is empty.");
DialogBox.ShowToastMessage(Game.GetLocTxt(LKeys.INVITEFAILED));
return false;
}
// Validate target user ID
if (string.IsNullOrEmpty(toUserID))
{
_logger.PrintLog("Cannot send invite: Target user OneSignal ID is null or empty.");
//DialogBox.ShowToastMessage("Cannot send invite. User is not available for notifications.");
DialogBox.ShowToastMessage(Game.GetLocTxt(LKeys.INVITEFAILED));
return false;
}
_logger.PrintLog(
$"InviteAsync toUserID: {toUserID}, message: {message}, roomCode: {roomCode}, playerName: {playerName}, senderPushID: {senderPushID}");
_logger.PrintLog($"Using App ID: {_appID}");
var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(30); // Set 30 second timeout to prevent hanging
var request = new HttpRequestMessage(HttpMethod.Post, "https://onesignal.com/api/v1/notifications");
// Use the correct authorization header format for OneSignal REST API
request.Headers.Add("Authorization", $"Basic {_apiAuthKey}");
request.Headers.Add("Accept", "application/json");
var pushOptions = new Dictionary<string, object>
{
["contents"] = new Dictionary<string, string> { ["en"] = message },
["data"] = new Dictionary<string, string>
{
["type"] = "Invitation",
["roomCode"] = roomCode,
["playerName"] = playerName,
["senderPushID"] = senderPushID,
},
["include_subscription_ids"] = new List<string> { toUserID },
["app_id"] = _appID
};
var jsonPayload = Json.Serialize(pushOptions);
_logger.PrintLog($"Request payload: {jsonPayload}");
var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
var resultMessage = await response.Content.ReadAsStringAsync();
_logger.PrintLog($"Response status: {response.StatusCode}");
_logger.PrintLog($"Response content: {resultMessage}");
if (response.IsSuccessStatusCode)
{
_logger.PrintLog("Successfully sent invite notification");
return true;
}
else
{
_logger.PrintLog($"Request failed with status code: {response.StatusCode} and message: {resultMessage}");
// Provide more specific error messages based on status code
// string userMessage = response.StatusCode switch
// {
// System.Net.HttpStatusCode.Unauthorized => "Authentication failed. Please check OneSignal configuration.",
// System.Net.HttpStatusCode.Forbidden => "Access denied. Please verify OneSignal API key permissions.",
// System.Net.HttpStatusCode.BadRequest => "Invalid request. Please check the notification data.",
// _ => "Invite failed. Please try again later."
// };
//DialogBox.ShowToastMessage(userMessage);
return false;
}
#endif
}
catch (TaskCanceledException e) when (e.InnerException is TimeoutException)
{
_logger.PrintLog($"Notifications Invite Timeout: {e.Message}");
DialogBox.ShowToastMessage(Game.GetLocTxt(LKeys.INVITEFAILED));
// DialogBox.ShowToastMessage("Request timed out. Please try again.");
return false;
}
catch (HttpRequestException e)
{
_logger.PrintLog($"Notifications Invite Network Error: {e.Message}");
DialogBox.ShowToastMessage(Game.GetLocTxt(LKeys.INVITEFAILED));
// DialogBox.ShowToastMessage("Network error. Please check your connection.");
return false;
}
catch (Exception e)
{
_logger.PrintLog($"Notifications Invite Failed: {e.Message}");
_logger.PrintLog($"Stack trace: {e.StackTrace}");
DialogBox.ShowToastMessage(Game.GetLocTxt(LKeys.INVITEFAILED));
// DialogBox.ShowToastMessage("Unexpected error occurred. Please try again.");
return false;
}
}
private async void SendInviteResponseAsync(string toUserID, string message, string response, string roomCode = "", string playerName = "")
{
try
{
#if USE_PUSH
// Validate configuration
if (string.IsNullOrEmpty(_appID) || string.IsNullOrEmpty(_apiAuthKey))
{
_logger.PrintLog("OneSignal configuration is missing. App ID or API Key is empty.");
return;
}
// Validate target user ID
if (string.IsNullOrEmpty(toUserID))
{
_logger.PrintLog("Cannot send invite response: Target user OneSignal ID is null or empty.");
return;
}
_logger.PrintLog(
$"SendInviteResponseAsync toUserID: {toUserID}, message: {message}, response: {response}, roomCode: {roomCode}, playerName: {playerName}");
var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(30); // Set 30 second timeout to prevent hanging
var request = new HttpRequestMessage(HttpMethod.Post, "https://onesignal.com/api/v1/notifications");
// Use the correct authorization header format for OneSignal REST API
request.Headers.Add("Authorization", $"Basic {_apiAuthKey}");
request.Headers.Add("Accept", "application/json");
var pushOptions = new Dictionary<string, object>
{
["contents"] = new Dictionary<string, string> { ["en"] = message },
["data"] = new Dictionary<string, string>
{
["type"] = "InvitationResponse",
["response"] = response,
["roomCode"] = roomCode,
["playerName"] = playerName
},
["include_subscription_ids"] = new List<string> { toUserID },
["app_id"] = _appID // Use the configured App ID instead of hardcoded value
};
var jsonPayload = Json.Serialize(pushOptions);
_logger.PrintLog($"Response payload: {jsonPayload}");
var content = new StringContent(jsonPayload, System.Text.Encoding.UTF8, "application/json");
request.Content = content;
var responseMessage = await client.SendAsync(request);
var resultMessage = await responseMessage.Content.ReadAsStringAsync();
_logger.PrintLog($"Response status: {responseMessage.StatusCode}");
_logger.PrintLog($"Response content: {resultMessage}");
if (!responseMessage.IsSuccessStatusCode)
{
_logger.PrintLog($"Failed to send invite response with status code: {responseMessage.StatusCode}");
}
else
{
_logger.PrintLog("Successfully sent invite response notification");
}
#endif
}
catch (TaskCanceledException e) when (e.InnerException is TimeoutException)
{
_logger.PrintLog($"SendInviteResponseAsync Timeout: {e.Message}");
}
catch (HttpRequestException e)
{
_logger.PrintLog($"SendInviteResponseAsync Network Error: {e.Message}");
}
catch (Exception e)
{
_logger.PrintLog($"SendInviteResponseAsync failed: {e.Message}");
_logger.PrintLog($"Stack trace: {e.StackTrace}");
}
}
private void ReloadGameSceneAndJoinTheGame()
{
StartCoroutine("ReloadGameSceneAndJoinTheGameNow");
}
IEnumerator ReloadGameSceneAndJoinTheGameNow()
{
Game.Log($"Reloading the game scene now to join the private game {Game.roomName}. isNewGame={Game.isNewGame}");
DialogBox.ShowLoadingScreen(Game.GetLocTxt(LKeys.PLEASEWAIT));
// Additional safeguard: Ensure isNewGame is true when reloading scene for invitations
// This prevents the black screen issue in Bug 143
if (Game.acceptingInvite && !Game.isNewGame)
{
Game.Log("ReloadGameSceneAndJoinTheGameNow: Force setting isNewGame=true for invitation flow");
Game.isNewGame = true;
}
yield return new WaitForSeconds(1f);
SceneManager.LoadSceneAsync(Game.GAMESCENENAME, LoadSceneMode.Single);
SceneManager.LoadSceneAsync(Game.GAMESCENEHUDNAME, LoadSceneMode.Additive);
Game.GCCollect();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment