535 lines
No EOL
16 KiB
C#
535 lines
No EOL
16 KiB
C#
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
using Analytics;
|
|
|
|
using Audio;
|
|
|
|
using ConsoleConnection;
|
|
|
|
using LightJSON;
|
|
|
|
using Network;
|
|
|
|
using Start;
|
|
|
|
using UI;
|
|
|
|
using DigitalRubyShared;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.Assertions;
|
|
|
|
public class UiManager : MonoBehaviour
|
|
{
|
|
// Singleton Instance
|
|
public static UiManager Instance { get; private set; }
|
|
|
|
public Canvas canvas;
|
|
|
|
public UiScreen CurrentUiScreen
|
|
{
|
|
get { return _currentUIScreen; }
|
|
}
|
|
|
|
// Screen layers (already loaded in the scene)
|
|
public UiLayer uiLayer;
|
|
public UiLayer gamePopupLayer;
|
|
public UiLayer waitingLayer;
|
|
public UiLayer appPopupLayer;
|
|
|
|
private UiLayer[] layers;
|
|
|
|
private JSON _disabledSetupData;
|
|
|
|
// this screens are the current ones as requested by the game, independent of transition/fade state
|
|
private UiScreen _currentUIScreen = null;
|
|
private UiScreen _currentGamePopupScreen = null;
|
|
|
|
// JD 19 inputs
|
|
private Dictionary<SwipeGestureRecognizerDirection, UnityAction> _callbackDir = new Dictionary<SwipeGestureRecognizerDirection, UnityAction>();
|
|
public Dictionary<SwipeGestureRecognizerDirection, UnityAction> CallbackDir {
|
|
get { return _callbackDir; }
|
|
}
|
|
|
|
private UnityAction _callbackTap;
|
|
private UnityAction _callbackLongPress;
|
|
|
|
public UnityAction CallbackTap {
|
|
get { return _callbackTap; }
|
|
}
|
|
|
|
public UnityAction CallbackLongPress {
|
|
get { return _callbackLongPress; }
|
|
}
|
|
|
|
private bool _longPress = false;
|
|
|
|
public bool LongPress {
|
|
get { return _longPress; }
|
|
}
|
|
|
|
private bool _invertDirection = false;
|
|
|
|
public string pairingCode;
|
|
|
|
public bool InvertDirection {
|
|
get {
|
|
return PlayerPrefsBool.GetBool("InverseAxis", _invertDirection);
|
|
}
|
|
set {
|
|
_invertDirection = value;
|
|
PlayerPrefsBool.SetBool("InverseAxis", _invertDirection);
|
|
}
|
|
}
|
|
|
|
public enum ShortcutType { NONE, GENERIC};
|
|
|
|
private void Awake()
|
|
{
|
|
Assert.IsNull(Instance);
|
|
Instance = this;
|
|
|
|
_canvasRoot = GameObject.Find("Canvas");
|
|
|
|
// switch to 60fps for a smooth experience
|
|
Application.targetFrameRate = 60;
|
|
|
|
layers = new[]
|
|
{
|
|
uiLayer,
|
|
gamePopupLayer,
|
|
waitingLayer,
|
|
appPopupLayer
|
|
};
|
|
}
|
|
|
|
#region GameObject LifeCycle
|
|
private void Start()
|
|
{
|
|
print("SystemInfo.deviceModel : " + SystemInfo.deviceModel);
|
|
|
|
// Setting the base value
|
|
_invertDirection = InvertDirection;
|
|
|
|
// Calling this early to set the UI correctly
|
|
_AdjustSafeAreaSpacers();
|
|
|
|
ConsoleNetworkManager.Instance.OnUIMessageReceived += UIMessageReceived;
|
|
ConsoleNetworkManager.Instance.OnShorcutNavMessageReceived += ShortcutNavMessageReceived;
|
|
ConsoleNetworkManager.Instance.OnSetupMessageReceived += SetupMessageReceived;
|
|
ConsoleNetworkManager.Instance.OnEnableCarousel += OnEnableCarousel;
|
|
ConsoleNetworkManager.Instance.OnOpenPhoneKeyboard += OnOpenPhoneKeyboard;
|
|
|
|
// Getting all the tap input from JD19
|
|
ConsoleNetworkManager.Instance.OnInputMessageReceived += InputMessageReceived;
|
|
|
|
// Knowing if it's a short tap / long tap for JD19
|
|
ConsoleNetworkManager.Instance.OnLongPressEnabled += LongPressEnabled;
|
|
|
|
Misc.PauseSlider.OnSlide += SendCommand;
|
|
SimpleButton.OnPress += SendCommand;
|
|
|
|
ScreenModel.Width = Screen.width / canvas.scaleFactor;
|
|
ScreenModel.Height = Screen.height / canvas.scaleFactor;
|
|
|
|
Statistics.LaunchTrackingSession();
|
|
|
|
var landingScreen = UiBuilder.Instance.BuildLandingScreen();
|
|
DisplayUIScreen(landingScreen, true);
|
|
|
|
Screen.autorotateToPortrait = true;
|
|
Screen.autorotateToPortraitUpsideDown = true;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (Input.GetKeyDown(KeyCode.Escape /* back button */))
|
|
{
|
|
HandleBackButton();
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
ConsoleNetworkManager.Instance.OnUIMessageReceived -= UIMessageReceived;
|
|
ConsoleNetworkManager.Instance.OnSetupMessageReceived -= SetupMessageReceived;
|
|
ConsoleNetworkManager.Instance.OnEnableCarousel -= OnEnableCarousel;
|
|
ConsoleNetworkManager.Instance.OnOpenPhoneKeyboard -= OnOpenPhoneKeyboard;
|
|
Misc.PauseSlider.OnSlide -= SendCommand;
|
|
SimpleButton.OnPress -= SendCommand;
|
|
|
|
// Removing the tap input from JD2019
|
|
ConsoleNetworkManager.Instance.OnInputMessageReceived -= InputMessageReceived;
|
|
|
|
// Removing the long / short press event
|
|
ConsoleNetworkManager.Instance.OnLongPressEnabled -= LongPressEnabled;
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
// (Almost) disable physics (set the update timestep to 10s)
|
|
Time.fixedDeltaTime = 10.0f;
|
|
|
|
// Disable screen dimming
|
|
Screen.sleepTimeout = SleepTimeout.NeverSleep;
|
|
}
|
|
#endregion
|
|
|
|
#region
|
|
|
|
private void HandleBackButton()
|
|
{
|
|
foreach (var layer in layers.Reverse())
|
|
{
|
|
// exit after the first layer that handled the back event
|
|
if (layer.HandleBackButton())
|
|
return;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Change layer contents
|
|
public void DisplayUIScreen(UiScreen newScreen, bool skipTransition = false)
|
|
{
|
|
_currentUIScreen = newScreen;
|
|
uiLayer.DisplayScreen(newScreen, 0.0f, skipTransition);
|
|
Statistics.NewCurrentScreen(newScreen.Name);
|
|
}
|
|
|
|
public void DisplayGamePopupScreen(UiScreen newScreen)
|
|
{
|
|
_currentGamePopupScreen = newScreen;
|
|
gamePopupLayer.DisplayScreen(newScreen);
|
|
}
|
|
|
|
public void DisplayWaitingScreen(UiScreen newScreen)
|
|
{
|
|
if ((_currentGamePopupScreen != null) && (_currentGamePopupScreen.gameObject.tag == "SearchFieldPopup"))
|
|
_currentGamePopupScreen.GetComponent<TextFieldPopup>().HideKeyboard();
|
|
|
|
waitingLayer.DisplayScreen(newScreen);
|
|
}
|
|
|
|
public void DisplayAppPopupScreen(UiScreen newScreen, float timeout = 0.0f, bool skipTransitionBetweenScreens = false, AudioManager.SoundIds popupSound = AudioManager.SoundIds.NoSound, bool showBackground = true)
|
|
{
|
|
if ((newScreen != null) && (popupSound != AudioManager.SoundIds.NoSound))
|
|
AudioManager.Instance.Play(popupSound);
|
|
appPopupLayer.DisplayScreen(newScreen, timeout, skipTransitionBetweenScreens, showBackground);
|
|
}
|
|
|
|
public void PrepareScreenTransition()
|
|
{
|
|
uiLayer.GetComponent<TransitionManager>().PrepareScreenTransition();
|
|
}
|
|
|
|
public void UndisplayTransition()
|
|
{
|
|
uiLayer.GetComponent<TransitionManager>().UndisplayTransition();
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Event called by the WebSocket Manager class by the WSMessageReceived event
|
|
private void UIMessageReceived(JSON message)
|
|
{
|
|
_disabledSetupData = JSONParseHelper.GetDisabledSetup(message);
|
|
|
|
var setupData = JSONParseHelper.GetSetupData(message);
|
|
if(setupData != null) {
|
|
var isPopup = JSONParseHelper.IsPopup(message);
|
|
|
|
var newScreen = UiBuilder.Instance.BuildGenericScreen(setupData, isPopup);
|
|
|
|
if(!isPopup) {
|
|
DisplayUIScreen(newScreen);
|
|
}
|
|
else {
|
|
DisplayGamePopupScreen(newScreen);
|
|
}
|
|
}
|
|
|
|
// UI messages always embed a setup message
|
|
var inputSetup = message.GetField("inputSetup");
|
|
if(inputSetup != null)
|
|
SetupMessageReceived(inputSetup);
|
|
}
|
|
|
|
private void ShortcutNavMessageReceived(JSON message)
|
|
{
|
|
UiBuilder.Instance.AddJD2019NavBar(CurrentUiScreen, message);
|
|
}
|
|
|
|
private void SetupMessageReceived(JSON setupMessage)
|
|
{
|
|
bool isSetupEnabled = JSONParseHelper.InputSetupEnabled(setupMessage);
|
|
bool applyOnPopup = JSONParseHelper.ApplyOnPopup(setupMessage);
|
|
|
|
if (isSetupEnabled)
|
|
{
|
|
DisplayWaitingScreen(null);
|
|
|
|
ConsoleCarouselController script = null;
|
|
if (applyOnPopup)
|
|
{
|
|
if (_currentGamePopupScreen != null)
|
|
script = _currentGamePopupScreen.GetComponentInChildren<ConsoleCarouselController>();
|
|
}
|
|
else
|
|
{
|
|
if (_currentUIScreen != null)
|
|
script = _currentUIScreen.GetComponentInChildren<ConsoleCarouselController>();
|
|
}
|
|
|
|
if (script != null)
|
|
{
|
|
int row = JSONParseHelper.GetPreselectedRow(setupMessage);
|
|
int item = JSONParseHelper.GetPreselectedItem(setupMessage);
|
|
int action = JSONParseHelper.GetPreselectedAction(setupMessage);
|
|
|
|
script.OnConsoleFocusMessage(row, item, action);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var waitingScreen = UiBuilder.Instance.BuildGenericScreen(_disabledSetupData);
|
|
DisplayWaitingScreen(waitingScreen);
|
|
}
|
|
}
|
|
|
|
private void InputMessageReceived(JSON message)
|
|
{
|
|
var availableInput = message.GetFieldAsObject("availableInput");
|
|
|
|
var rightInputMsg = JSON.Parse(availableInput.GetFieldAsString("RIGHT"));
|
|
var leftInputMsg = JSON.Parse(availableInput.GetFieldAsString("LEFT"));
|
|
var upInputMsg = JSON.Parse(availableInput.GetFieldAsString("UP"));
|
|
var downInputMsg = JSON.Parse(availableInput.GetFieldAsString("DOWN"));
|
|
|
|
var validInputMsg = JSON.Parse(availableInput.GetFieldAsString("VALID"));
|
|
|
|
var consoleNetworkManager = ConsoleNetworkManager.Instance;
|
|
// Directions
|
|
_callbackDir.Clear();
|
|
_callbackDir.Add(SwipeGestureRecognizerDirection.Right, () => {
|
|
consoleNetworkManager.Send(rightInputMsg);
|
|
});
|
|
|
|
_callbackDir.Add(SwipeGestureRecognizerDirection.Left, () => {
|
|
consoleNetworkManager.Send(leftInputMsg);
|
|
});
|
|
|
|
_callbackDir.Add(SwipeGestureRecognizerDirection.Up, () => {
|
|
consoleNetworkManager.Send(upInputMsg);
|
|
});
|
|
|
|
_callbackDir.Add(SwipeGestureRecognizerDirection.Down, () => {
|
|
consoleNetworkManager.Send(downInputMsg);
|
|
});
|
|
|
|
// Short tap
|
|
_callbackTap = () => {
|
|
if(!LongPress) {
|
|
consoleNetworkManager.Send(validInputMsg);
|
|
}
|
|
};
|
|
|
|
// Long tap
|
|
_callbackLongPress = () => {
|
|
if(LongPress) {
|
|
consoleNetworkManager.Send(validInputMsg);
|
|
}
|
|
};
|
|
}
|
|
|
|
private void LongPressEnabled(bool longPress)
|
|
{
|
|
_longPress = longPress;
|
|
}
|
|
|
|
private void OnEnableCarousel(bool enabled, bool applyOnPopup)
|
|
{
|
|
Carousel.Carousel carousel = null;
|
|
if (applyOnPopup)
|
|
{
|
|
if (_currentGamePopupScreen != null)
|
|
carousel = _currentGamePopupScreen.GetComponentInChildren<Carousel.Carousel>();
|
|
}
|
|
else
|
|
{
|
|
if (_currentUIScreen != null)
|
|
carousel = _currentUIScreen.GetComponentInChildren<Carousel.Carousel>();
|
|
}
|
|
|
|
if (carousel != null)
|
|
carousel.SetEnabled(enabled);
|
|
}
|
|
|
|
#region landing
|
|
|
|
public void DisplayLanding()
|
|
{
|
|
var newScreen = UiBuilder.Instance.BuildLandingScreen();
|
|
DisplayUIScreen(newScreen);
|
|
}
|
|
|
|
public void ShowControllerTutorial()
|
|
{
|
|
var newScreen = UiBuilder.Instance.BuildControllerTutorialScreen();
|
|
DisplayAppPopupScreen(newScreen);
|
|
}
|
|
|
|
public void ShowSettings(bool fromGame = false)
|
|
{
|
|
var newScreen = UiBuilder.Instance.BuildSettingsScreen(fromGame);
|
|
DisplayAppPopupScreen(newScreen, 0, false, AudioManager.SoundIds.ConfigPanelOpen, true);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Carousels
|
|
private void SendCommand(JSON command)
|
|
{
|
|
ConsoleNetworkManager.Instance.Send(command);
|
|
}
|
|
|
|
private void OnOpenPhoneKeyboard(bool isPassword)
|
|
{
|
|
var inputScreen = UiBuilder.Instance.BuildSearchFieldPopup(isPassword);
|
|
DisplayGamePopupScreen(inputScreen);
|
|
}
|
|
#endregion
|
|
|
|
private void OnApplicationPause(bool pauseStatus)
|
|
{
|
|
if (pauseStatus)
|
|
{
|
|
// app is going to background
|
|
Statistics.SuspendTimers();
|
|
}
|
|
else
|
|
{
|
|
// coming back to foreground
|
|
Statistics.ResumeTimers();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// UI Tools
|
|
// -----------------------------------------------------------------------------------------
|
|
|
|
public string ChangePairingCodeToHyphen(string pairingCode)
|
|
{
|
|
if(string.IsNullOrEmpty(pairingCode) || pairingCode.Length != 6)
|
|
{
|
|
return pairingCode;
|
|
}
|
|
|
|
return pairingCode = System.Text.RegularExpressions.Regex.Replace(pairingCode, ".{2}(?!$)", "$0<color=#afafafff>-</color>");
|
|
}
|
|
// SAFE AREA
|
|
|
|
private Rect _safeArea;
|
|
|
|
// spacer heights
|
|
private float _topSpacerH;
|
|
private float _bottomSpacerH;
|
|
|
|
public float TopSpacerH {
|
|
get { return _topSpacerH; }
|
|
}
|
|
|
|
public float BottomSpacerH {
|
|
get { return _bottomSpacerH; }
|
|
}
|
|
|
|
public float GetTotalVerticalSpacer()
|
|
{
|
|
return _topSpacerH + _bottomSpacerH;
|
|
}
|
|
|
|
// UI root
|
|
private GameObject _canvasRoot = null;
|
|
|
|
// used by safe area spacers components
|
|
public enum SpacerType
|
|
{
|
|
None = 0,
|
|
Top = 1,
|
|
Left = 2,
|
|
Bottom = 3,
|
|
Right = 4
|
|
}
|
|
|
|
// minimal value to take a vertical safe area into account
|
|
// its a little arbitrary so we don't bother with rounded corner screens all the time
|
|
|
|
private const float SAFE_AREA_HEIGHT_THRESHOLD = 28f;
|
|
|
|
// Will force a RectTransform to be fullscreen regardless of SafeArea
|
|
// Usefull for alpha rect transform that needs to take all space, preventing strange borders
|
|
public void SetRectFullSize(RectTransform rectTransform)
|
|
{
|
|
// iphone 6+ is unreliable here
|
|
if(_topSpacerH <= 0 || _bottomSpacerH <= 0)
|
|
return;
|
|
|
|
rectTransform.offsetMax = new Vector2(0f, _topSpacerH);
|
|
rectTransform.offsetMin = new Vector2(0f, -_bottomSpacerH);
|
|
}
|
|
|
|
private void _AdjustSafeAreaSpacers()
|
|
{
|
|
// safe area management
|
|
_safeArea = Screen.safeArea;
|
|
|
|
var topSpacerHCalc = _safeArea.yMin;
|
|
var bottomSpacerHCalc = Screen.height - _safeArea.yMax;
|
|
|
|
#if UNITY_IOS
|
|
// Edge case.
|
|
if(UnityEngine.iOS.Device.generation == UnityEngine.iOS.DeviceGeneration.iPhone7 ||
|
|
UnityEngine.iOS.Device.generation == UnityEngine.iOS.DeviceGeneration.iPhone7Plus) {
|
|
topSpacerHCalc = 0f;
|
|
bottomSpacerHCalc = 0f;
|
|
}
|
|
#endif
|
|
// compute top and bottom spacers height so content matches the safe area
|
|
_topSpacerH = ((topSpacerHCalc >= SAFE_AREA_HEIGHT_THRESHOLD) ? topSpacerHCalc : 0f) / 2;
|
|
_bottomSpacerH = ((bottomSpacerHCalc >= SAFE_AREA_HEIGHT_THRESHOLD) ? bottomSpacerHCalc : 0f) / 2;
|
|
|
|
// fetch all safe area spacers
|
|
var spacers = _canvasRoot.gameObject.GetComponentsInChildren<SafeAreaSpacer>(true);
|
|
if(spacers.Length == 0) {
|
|
Debug.Log("UIManager: no safe area spacers detected.");
|
|
return;
|
|
}
|
|
// walk found spacers to assign them proper spacing dimensions
|
|
for(var i = 0; i < spacers.Length; i++) {
|
|
var spacer = spacers[i];
|
|
|
|
// no particular check cause LayoutElement is required by SafeAreaSpacer
|
|
var layout = spacer.GetComponent<UnityEngine.UI.LayoutElement>();
|
|
|
|
if(spacer.spacerType == SpacerType.None) {
|
|
Debug.LogError("UIManager: no SpacerType defined for SafeAreaSpacer " + spacer.name + ".");
|
|
return;
|
|
}
|
|
|
|
if(spacer.spacerType == SpacerType.Top) {
|
|
// top spacer
|
|
layout.preferredHeight = _topSpacerH;
|
|
}
|
|
else if(spacer.spacerType == SpacerType.Bottom) {
|
|
// bottom spacer
|
|
layout.preferredHeight = _bottomSpacerH;
|
|
}
|
|
else {
|
|
// horizontal spacer
|
|
Debug.LogError("UIManager: Horizontal spacers not managed!");
|
|
}
|
|
}
|
|
}
|
|
} |