JD2022-TU1/main/phone_apps/JDCompanionApp/Assets/Scripts/UiManager.cs

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!");
}
}
}
}