import class gameuiNewPhoneRelatedGameController extends inkGameController
{
	protected import var isNewPhoneEnabled : Bool;
}

import class gameuiNewPhoneRelatedHUDGameController extends inkHUDGameController
{
	protected var m_player : weak< PlayerPuppet >;
	protected var m_isRemoteControllingVehicle : Bool;
	protected var m_visibilityFact1ListenerId : Uint32;
	protected var m_visibilityFact2ListenerId : Uint32;
	protected var m_remoteControlledVehicleDataCallback : CallbackHandle;
	protected import var isNewPhoneEnabled : Bool;

	protected event OnPlayerAttach( player : GameObject )
	{
		m_player = ( ( PlayerPuppet )( player ) );
	}

	protected function RegisterFactVisibilityListeners()
	{
		m_visibilityFact1ListenerId = GameInstance.GetQuestsSystem( m_player.GetGame() ).RegisterListener( 'dpad_hints_visibility_enabled', this, 'OnConsumableTutorial' );
		m_visibilityFact2ListenerId = GameInstance.GetQuestsSystem( m_player.GetGame() ).RegisterListener( 'q000_started', this, 'OnGameStarted' );
	}

	protected function UnregisterFactVisibilityListeners()
	{
		GameInstance.GetQuestsSystem( m_player.GetGame() ).UnregisterListener( 'dpad_hints_visibility_enabled', m_visibilityFact1ListenerId );
		GameInstance.GetQuestsSystem( m_player.GetGame() ).UnregisterListener( 'q000_started', m_visibilityFact2ListenerId );
	}

	protected function RegisterCommonBlackboardListeners()
	{
		var blackboardSystem : BlackboardSystem;
		var uiActiveVehicleBlackboard : weak< IBlackboard >;
		blackboardSystem = GetBlackboardSystem();
		uiActiveVehicleBlackboard = blackboardSystem.Get( GetAllBlackboardDefs().UI_ActiveVehicleData );
		m_remoteControlledVehicleDataCallback = uiActiveVehicleBlackboard.RegisterListenerVariant( GetAllBlackboardDefs().UI_ActiveVehicleData.RemoteControlledVehicleData, this, 'OnRemoteControlledVehicleChanged', true );
	}

	protected function UnregisterCommonBlackboardListeners()
	{
		var blackboardSystem : BlackboardSystem;
		var uiActiveVehicleBlackboard : weak< IBlackboard >;
		blackboardSystem = GetBlackboardSystem();
		uiActiveVehicleBlackboard = blackboardSystem.Get( GetAllBlackboardDefs().UI_ActiveVehicleData );
		if( m_remoteControlledVehicleDataCallback )
		{
			uiActiveVehicleBlackboard.UnregisterListenerVariant( GetAllBlackboardDefs().UI_ActiveVehicleData.RemoteControlledVehicleData, m_remoteControlledVehicleDataCallback );
		}
	}

	protected function OnGameStarted( value : Int32 )
	{
		UpdateVisibility();
	}

	protected function OnConsumableTutorial( value : Int32 )
	{
		UpdateVisibility();
	}

	protected function UpdateVisibility()
	{
		if( GameStarted() )
		{
			GetRootWidget().SetVisible( IsDerivedHUDVisible() && TutorialActivated() );
		}
		else
		{
			if( !( TutorialActivated() ) )
			{
				GetRootWidget().SetVisible( IsDerivedHUDVisible() );
			}
		}
	}

	protected virtual function IsDerivedHUDVisible() : Bool
	{
		return true;
	}

	protected virtual function UpdateCurrentItem() {}

	protected event OnRemoteControlledVehicleChanged( value : Variant )
	{
		var data : RemoteControlDrivingUIData;
		data = ( ( RemoteControlDrivingUIData )value );
		m_isRemoteControllingVehicle = data.remoteControlledVehicle != NULL;
		UpdateVisibility();
		UpdateCurrentItem();
	}

	protected function GameStarted() : Bool
	{
		var qs : QuestsSystem;
		qs = GameInstance.GetQuestsSystem( m_player.GetGame() );
		if( qs )
		{
			return qs.GetFact( 'q000_started' );
		}
		return false;
	}

	protected function TutorialActivated() : Bool
	{
		var qs : QuestsSystem;
		qs = GameInstance.GetQuestsSystem( m_player.GetGame() );
		if( qs )
		{
			return qs.GetFact( 'dpad_hints_visibility_enabled' );
		}
		return false;
	}

	protected function CanUseOverclock() : Bool
	{
		return QuickHackableHelper.CanActivateOverclockedState( m_player );
	}

}

import class gameuiNewHudPhoneGameController extends gameuiGenericNotificationGameController
{
	protected import function OpenSmsMessenger( data : inkGameNotificationData );
	protected import function CloseSmsMessenger();

	protected import var holoAudioCallElement : gameuiLocalPhoneElement;
	protected import var incomingCallElement : gameuiLocalPhoneElement;
	protected import var contactsElement : gameuiLocalPhoneElement;
	protected import var phoneIconElement : gameuiLocalPhoneElement;
}

import enum gameuiActivePhoneElement
{
	Call,
	IncomingCall,
	Contacts,
	SmsMessenger,
	Notifications,
	InVehicle,
	None,
}

import enum worlduiEntryVisibility
{
	TierVisibility,
	ForceShow,
	ForceHide,
}

importonly struct gameuiPhoneElementVisibility
{
	import var slot : inkCompoundRef;
}

importonly struct gameuiLocalPhoneElement extends gameuiPhoneElementVisibility
{
	import var libraryID : CName;
	import var request : weak< inkAsyncSpawnRequest >;
	import var widget : weak< inkWidget >;
}

enum PhoneScreenType
{
	Unread = 0,
	Contacts = 1,
}

enum MessageHash
{
	Invalid = 0,
	Fake = -1,
}

class ContactDataHelper
{

	public static function Contains( data : ref< array< IScriptable > >, hashToFind : Int32 ) : Bool
	{
		var i, j : Int32;
		var contact : ContactData;
		var entryHash : Int32;
		if( hashToFind == 0 )
		{
			return false;
		}
		for( i = 0; i < data.Size(); i += 1 )
		{
			contact = ( ( ContactData )( data[ i ] ) );
			if( ( contact.hash == hashToFind ) || ( contact.conversationHash == hashToFind ) )
			{
				return true;
			}
			for( j = 0; j < contact.unreadMessages.Size(); j += 1 )
			{
				entryHash = contact.unreadMessages[ j ];
				if( entryHash == hashToFind )
				{
					return true;
				}
			}
		}
		return false;
	}

	public static function IndexOfOrZero( dataView : weak< DialerContactDataView >, hash : Int32 ) : Uint32
	{
		var i : Uint32;
		var j : Int32;
		var contact : ContactData;
		var entryHash : Int32;
		if( hash == 0 )
		{
			return 0;
		}
		for( i = 0; i < dataView.Size(); i += 1 )
		{
			contact = ( ( ContactData )( dataView.GetItem( i ) ) );
			if( ( contact.hash == hash ) || ( contact.conversationHash == hash ) )
			{
				return i;
			}
			for( j = 0; j < contact.unreadMessages.Size(); j += 1 )
			{
				entryHash = contact.unreadMessages[ j ];
				if( entryHash == hash )
				{
					return i;
				}
			}
		}
		return 0;
	}

	public static function FindClosestContactWithUnread( dataView : weak< DialerContactDataView >, startIndex : Int32 ) : ContactData
	{
		var a : Int32;
		var b : Int32;
		var aContact : ContactData;
		var bContact : ContactData;
		var size : Int32;
		a = startIndex;
		b = startIndex;
		size = ( ( Int32 )( dataView.Size() ) ) - 1;
		while( ( a != 0 ) && ( b != size ) )
		{
			a = Clamp( a - 1, 0, size );
			b = Clamp( b + 1, 0, size );
			aContact = ( ( ContactData )( dataView.GetItem( ( ( Uint32 )( a ) ) ) ) );
			bContact = ( ( ContactData )( dataView.GetItem( ( ( Uint32 )( b ) ) ) ) );
			if( ( aContact.unreadMessages.Size() > 0 ) || aContact.playerCanReply )
			{
				return aContact;
			}
			else if( ( bContact.unreadMessages.Size() > 0 ) || bContact.playerCanReply )
			{
				return bContact;
			}
		}
		return NULL;
	}

	public static function FetchContactHash( contactData : weak< ContactData > ) : Int32
	{
		if( !( contactData ) )
		{
			return 0;
		}
		return ( ( contactData.type == MessengerContactType.MultiThread ) ? ( contactData.conversationHash ) : ( contactData.hash ) );
	}

}

class NewHudPhoneGameController extends gameuiNewHudPhoneGameController
{
	var m_player : weak< PlayerPuppet >;
	var m_journalMgr : weak< JournalManager >;
	var m_questsSystem : weak< QuestsSystem >;
	var m_uiSystem : weak< UISystem >;
	var m_fact1ListenerId : Uint32;
	var m_fact2ListenerId : Uint32;
	var m_fact3ListenerId : Uint32;
	var m_onNotificationsQueueChanged : CallbackHandle;
	var m_currActiveQueueId : Int32;
	default m_currActiveQueueId = -1;
	var m_CurrentFunction : EHudPhoneFunction;
	var m_gameplayRestrictions : array< CName >;
	var m_buttonPressed : Bool;
	default m_buttonPressed = false;
	var m_repeatingScrollActionEnabled : Bool;
	default m_repeatingScrollActionEnabled = false;
	var m_TimeoutPeroid : Float;
	default m_TimeoutPeroid = 8.0f;
	var m_activePhoneElements : Uint32;
	var m_bbSystem : weak< BlackboardSystem >;
	var m_bbUiSystemDef : UI_SystemDef;
	var m_bbUiSystem : weak< IBlackboard >;
	var m_isInMenuCallback : CallbackHandle;
	var m_bbUiComDeviceDef : UI_ComDeviceDef;
	var m_bbUiComDevice : weak< IBlackboard >;
	var m_phoneCallInformationCallback : CallbackHandle;
	var m_phoneStatusChangedCallback : CallbackHandle;
	var m_phoneMinimizedCallback : CallbackHandle;
	var m_contactsActiveCallback : CallbackHandle;
	var m_messageToOpenCallback : CallbackHandle;
	var m_phoneEnabledBBId : CallbackHandle;
	var m_bbUiQuickSlotsDataDef : UI_QuickSlotsDataDef;
	var m_bbUiQuickSlotsData : weak< IBlackboard >;
	var m_bbUiPlayerStatsDef : UI_PlayerStatsDef;
	var m_bbUiPlayerStats : weak< IBlackboard >;
	var m_DelaySystem : weak< DelaySystem >;
	var m_DelayedTimeoutCallbackId : DelayID;
	var m_PhoneSystem : weak< PhoneSystem >;
	var m_CurrentCallInformation : PhoneCallInformation;
	var m_CurrentPhoneCallContact : weak< JournalContact >;
	var m_holoAudioCallLogicController : weak< HoloAudioCallLogicController >;
	var m_contactListLogicController : weak< PhoneDialerLogicController >;
	var m_phoneIconAnimProxy : inkAnimProxy;
	var m_backgroundAnimProxy : inkAnimProxy;
	var m_screenType : PhoneScreenType;
	var m_messagesPanelVisible : Bool;
	default m_messagesPanelVisible = false;
	var m_messagesPanelSpawned : Bool;
	default m_messagesPanelSpawned = false;
	var m_threadsVisible : Bool;
	default m_threadsVisible = false;
	var m_messageToOpenHash : Int32;
	var m_indexToSelect : Uint32;
	var m_isSingleThread : Bool;
	var m_isShowingAllMessages : Bool;
	default m_isShowingAllMessages = false;
	var m_keepOpenWhenInHubMenu : Bool;
	default m_keepOpenWhenInHubMenu = false;
	var m_audioSystem : weak< AudioSystem >;
	private var m_isRemoteControllingDevice : Bool;
	private var m_psmIsControllingDeviceCallback : CallbackHandle;
	private var m_vehicleEnterCallback : CallbackHandle;

	public export override function GetShouldSaveState() : Bool
	{
		return true;
	}

	public export override function GetID() : Int32
	{
		return ( ( Int32 )( GenericNotificationType.PhoneNotification ) );
	}

	protected event OnInitialize()
	{
		var psmBlackboard : weak< IBlackboard >;
		var uiBlackboard : weak< IBlackboard >;
		var request : questTriggerCallRequest;
		var infoVariant : Variant;
		var lastPhoneCallInformation : PhoneCallInformation;
		if( !( phoneIconElement.request ) )
		{
			phoneIconElement.request = AsyncSpawnFromLocal( inkCompoundRef.Get( phoneIconElement.slot ), phoneIconElement.libraryID, this, 'OnPhoneIconSpawned' );
		}
		m_player = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		m_journalMgr = GameInstance.GetJournalManager( m_player.GetGame() );
		if( m_journalMgr )
		{
			m_journalMgr.RegisterScriptCallback( this, 'OnJournalUpdate', gameJournalListenerType.State );
			m_journalMgr.RegisterScriptCallback( this, 'OnJournalEntryVisited', gameJournalListenerType.Visited );
		}
		m_questsSystem = GameInstance.GetQuestsSystem( m_player.GetGame() );
		if( m_questsSystem )
		{
			m_fact1ListenerId = m_questsSystem.RegisterListener( 'dpad_hints_visibility_enabled', this, 'OnConsumableTutorial' );
			m_fact2ListenerId = m_questsSystem.RegisterListener( 'q000_started', this, 'OnGameStarted' );
			m_fact3ListenerId = m_questsSystem.RegisterListener( 'dpad_hint_phone_visible', this, 'OnDpadVisibilityChanged' );
		}
		uiBlackboard = GetUIBlackboard();
		if( uiBlackboard )
		{
			m_onNotificationsQueueChanged = uiBlackboard.RegisterListenerInt( GetAllBlackboardDefs().UIGameData.ActiveNotificationsQueue, this, 'OnNotificationsQueueChanged' );
		}
		m_bbSystem = GetBlackboardSystem();
		psmBlackboard = m_bbSystem.GetLocalInstanced( m_player.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		if( psmBlackboard )
		{
			m_psmIsControllingDeviceCallback = psmBlackboard.RegisterListenerBool( GetAllBlackboardDefs().PlayerStateMachine.IsControllingDevice, this, 'OnPSMIsControllingDeviceChanged', true );
			m_vehicleEnterCallback = psmBlackboard.RegisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Vehicle, this, 'OnPlayerEnteredVehicle', true );
		}
		m_bbUiSystemDef = GetAllBlackboardDefs().UI_System;
		m_bbUiSystem = m_bbSystem.Get( m_bbUiSystemDef );
		if( m_bbUiSystem )
		{
			m_isInMenuCallback = m_bbUiSystem.RegisterDelayedListenerBool( m_bbUiSystemDef.IsInMenu, this, 'OnMenuUpdate' );
			m_bbUiSystem.SignalBool( m_bbUiSystemDef.IsInMenu );
		}
		m_bbUiComDeviceDef = GetAllBlackboardDefs().UI_ComDevice;
		m_bbUiComDevice = m_bbSystem.Get( m_bbUiComDeviceDef );
		if( m_bbUiComDevice )
		{
			m_phoneCallInformationCallback = m_bbUiComDevice.RegisterDelayedListenerVariant( m_bbUiComDeviceDef.PhoneCallInformation, this, 'OnPhoneCall' );
			m_phoneStatusChangedCallback = m_bbUiComDevice.RegisterDelayedListenerName( m_bbUiComDeviceDef.comDeviceSetStatusText, this, 'OnPhoneStatusChanged' );
			m_phoneMinimizedCallback = m_bbUiComDevice.RegisterDelayedListenerBool( m_bbUiComDeviceDef.PhoneStyle_Minimized, this, 'OnPhoneMinimized' );
			m_contactsActiveCallback = m_bbUiComDevice.RegisterDelayedListenerBool( m_bbUiComDeviceDef.ContactsActive, this, 'OnContactsActive' );
			m_messageToOpenCallback = m_bbUiComDevice.RegisterDelayedListenerInt( m_bbUiComDeviceDef.MessageToOpenHash, this, 'OnMessageToOpenHashChanged' );
			m_phoneEnabledBBId = m_bbUiComDevice.RegisterListenerBool( m_bbUiComDeviceDef.PhoneEnabled, this, 'OnPhoneEnabledChanged' );
			infoVariant = m_bbUiComDevice.GetVariant( m_bbUiComDeviceDef.PhoneCallInformation );
			if( infoVariant.IsValid() )
			{
				lastPhoneCallInformation = ( ( PhoneCallInformation )infoVariant );
				if( lastPhoneCallInformation.callPhase == questPhoneCallPhase.IncomingCall || lastPhoneCallInformation.callPhase == questPhoneCallPhase.StartCall )
				{
					request = CreateTriggerCallRequestFromPhoneCallInformation( lastPhoneCallInformation );
					GameInstance.GetScriptableSystemsContainer( m_player.GetGame() ).QueueRequest( request );
				}
			}
		}
		m_bbUiQuickSlotsDataDef = GetAllBlackboardDefs().UI_QuickSlotsData;
		m_bbUiQuickSlotsData = m_bbSystem.Get( m_bbUiQuickSlotsDataDef );
		m_bbUiPlayerStatsDef = GetAllBlackboardDefs().UI_PlayerStats;
		m_bbUiPlayerStats = m_bbSystem.Get( m_bbUiPlayerStatsDef );
		m_uiSystem = GameInstance.GetUISystem( m_player.GetGame() );
		m_DelaySystem = GameInstance.GetDelaySystem( m_player.GetGame() );
		m_PhoneSystem = ( ( PhoneSystem )( GameInstance.GetScriptableSystemsContainer( m_player.GetGame() ).Get( 'PhoneSystem' ) ) );
		m_audioSystem = GameInstance.GetAudioSystem( m_player.GetGame() );
		CachePredefinedRestrictions();
		SetPhoneFunction( EHudPhoneFunction.Inactive );
		PopupStateUtils.SetBackgroundBlurBlendTime( this, 0.1 );
		SetNotificationPauseWhenHidden( true );
		ResolveVisibility();
	}

	protected event OnUninitialize()
	{
		var uiBlackboard : IBlackboard;
		if( m_journalMgr )
		{
			m_journalMgr.UnregisterScriptCallback( this, 'OnJournalUpdate' );
			m_journalMgr.UnregisterScriptCallback( this, 'OnJournalEntryVisited' );
		}
		if( m_questsSystem )
		{
			m_questsSystem.UnregisterListener( 'dpad_hints_visibility_enabled', m_fact1ListenerId );
			m_questsSystem.UnregisterListener( 'q000_started', m_fact2ListenerId );
			m_questsSystem.UnregisterListener( 'dpad_hint_phone_visible', m_fact3ListenerId );
		}
		if( m_psmIsControllingDeviceCallback )
		{
			GetBlackboardSystem().GetLocalInstanced( m_player.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine ).UnregisterListenerBool( GetAllBlackboardDefs().PlayerStateMachine.IsControllingDevice, m_psmIsControllingDeviceCallback );
		}
		if( m_vehicleEnterCallback )
		{
			GetBlackboardSystem().GetLocalInstanced( m_player.GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine ).UnregisterListenerInt( GetAllBlackboardDefs().PlayerStateMachine.Vehicle, m_vehicleEnterCallback );
		}
		uiBlackboard = GetUIBlackboard();
		if( uiBlackboard )
		{
			uiBlackboard.UnregisterListenerInt( GetAllBlackboardDefs().UIGameData.ActiveNotificationsQueue, m_onNotificationsQueueChanged );
		}
		if( m_bbUiSystem )
		{
			m_bbUiSystem.UnregisterDelayedListener( m_bbUiSystemDef.IsInMenu, m_isInMenuCallback );
		}
		if( m_bbUiComDevice )
		{
			m_bbUiComDevice.UnregisterDelayedListener( m_bbUiComDeviceDef.PhoneCallInformation, m_phoneCallInformationCallback );
			m_bbUiComDevice.UnregisterDelayedListener( m_bbUiComDeviceDef.comDeviceSetStatusText, m_phoneStatusChangedCallback );
			m_bbUiComDevice.UnregisterDelayedListener( m_bbUiComDeviceDef.PhoneStyle_Minimized, m_phoneMinimizedCallback );
			m_bbUiComDevice.UnregisterDelayedListener( m_bbUiComDeviceDef.ContactsActive, m_contactsActiveCallback );
			m_bbUiComDevice.UnregisterDelayedListener( m_bbUiComDeviceDef.MessageToOpenHash, m_messageToOpenCallback );
			m_bbUiComDevice.UnregisterListenerBool( m_bbUiComDeviceDef.PhoneEnabled, m_phoneEnabledBBId );
		}
	}

	protected event OnPlayerAttach( playerPuppet : GameObject )
	{
		playerPuppet.RegisterInputListener( this, 'PhoneInteract' );
		playerPuppet.RegisterInputListener( this, 'PhoneReject' );
	}

	protected event OnPlayerDetach( playerPuppet : GameObject )
	{
		playerPuppet.UnregisterInputListener( this );
	}

	protected event OnPlayerEnteredVehicle( value : Int32 )
	{
		if( ( value != ( ( Int32 )( gamePSMVehicle.Default ) ) ) && ( value != ( ( Int32 )( gamePSMVehicle.Transition ) ) ) )
		{
			ActivatePhoneElement( gameuiActivePhoneElement.InVehicle );
		}
		else
		{
			DeactivatePhoneElement( gameuiActivePhoneElement.InVehicle );
		}
	}

	public function OnPhoneEnabledChanged( enabled : Bool )
	{
		if( !( enabled ) && IsPhoneActive() )
		{
			CloseContactList();
		}
	}

	private function IsPhoneActive() : Bool
	{
		if( m_bbUiComDevice )
		{
			return m_bbUiComDevice.GetBool( m_bbUiComDeviceDef.ContactsActive );
		}
		return false;
	}

	protected event OnAction( action : ListenerAction, consumer : ListenerActionConsumer )
	{
		var actionName : CName;
		var actionType : gameinputActionType;
		var wheelOpened : Bool;
		var hacksOpened : Bool;
		var uiActionPerformed : DPADActionPerformed;
		var pickupRequest : PickupPhoneRequest;
		actionName = ListenerAction.GetName( action );
		actionType = ListenerAction.GetType( action );
		wheelOpened = m_bbUiQuickSlotsData.GetBool( m_bbUiQuickSlotsDataDef.UIRadialContextRequest );
		hacksOpened = m_bbUiQuickSlotsData.GetBool( m_bbUiQuickSlotsDataDef.quickhackPanelOpen );
		if( actionName == 'PhoneInteract' )
		{
			uiActionPerformed = new DPADActionPerformed;
			uiActionPerformed.action = EHotkey.DPAD_DOWN;
			if( actionType == gameinputActionType.BUTTON_PRESSED )
			{
				if( m_player.PlayerLastUsedPad() )
				{
					uiActionPerformed.state = EUIActionState.STARTED;
					uiActionPerformed.successful = true;
					m_buttonPressed = true;
					QueueEvent( uiActionPerformed );
				}
			}
			else if( actionType == gameinputActionType.BUTTON_RELEASED )
			{
				if( m_CurrentCallInformation.callPhase == questPhoneCallPhase.IncomingCall )
				{
					pickupRequest = new PickupPhoneRequest;
					pickupRequest.CallInformation = m_CurrentCallInformation;
					m_PhoneSystem.QueueRequest( pickupRequest );
					return true;
				}
				else if( m_buttonPressed )
				{
					m_buttonPressed = false;
					uiActionPerformed.state = EUIActionState.ABORTED;
					uiActionPerformed.successful = false;
					QueueEvent( uiActionPerformed );
				}
			}
			if( actionType == gameinputActionType.BUTTON_HOLD_COMPLETE && m_CurrentCallInformation.callPhase != questPhoneCallPhase.IncomingCall )
			{
				if( !( wheelOpened ) && !( hacksOpened ) )
				{
					if( !( m_bbUiPlayerStats.GetBool( m_bbUiPlayerStatsDef.isReplacer ) ) )
					{
						m_PhoneSystem.QueueRequest( new UsePhoneRequest );
						uiActionPerformed.successful = true;
						uiActionPerformed.state = EUIActionState.COMPLETED;
						m_buttonPressed = false;
						QueueEvent( uiActionPerformed );
						return true;
					}
				}
				else
				{
					QueueEvent( uiActionPerformed );
				}
			}
		}
		else if( actionName == 'PhoneReject' )
		{
			if( actionType == gameinputActionType.BUTTON_HOLD_COMPLETE && m_CurrentCallInformation.callPhase == questPhoneCallPhase.IncomingCall )
			{
				pickupRequest = new PickupPhoneRequest;
				pickupRequest.CallInformation = m_CurrentCallInformation;
				pickupRequest.shouldBeRejected = true;
				m_PhoneSystem.QueueRequest( pickupRequest );
			}
		}
		else if( m_contactListLogicController )
		{
			return OnContactListAction( action, consumer );
		}
	}

	protected event OnContactListAction( action : ListenerAction, consumer : ListenerActionConsumer )
	{
		var actionName : CName;
		var actionType : gameinputActionType;
		actionName = ListenerAction.GetName( action );
		actionType = ListenerAction.GetType( action );
		if( actionType == gameinputActionType.REPEAT )
		{
			if( !( m_repeatingScrollActionEnabled ) )
			{
				return false;
			}
			switch( actionName )
			{
				case 'popup_moveDown':
					m_contactListLogicController.NavigateDown();
				break;
				case 'popup_moveUp':
					m_contactListLogicController.NavigateUp();
				break;
				case 'popup_moveUp_left_stick_down':
					m_contactListLogicController.NavigateDown();
				break;
				case 'popup_moveUp_left_stick_up':
					m_contactListLogicController.NavigateUp();
				break;
			}
		}
		else if( actionType == gameinputActionType.BUTTON_PRESSED )
		{
			if( !( m_repeatingScrollActionEnabled ) )
			{
				m_repeatingScrollActionEnabled = true;
			}
			switch( actionName )
			{
				case 'popup_moveDown':
					m_contactListLogicController.NavigateDown();
				break;
				case 'popup_moveUp':
					m_contactListLogicController.NavigateUp();
				break;
				case 'popup_moveUp_left_stick_down':
					m_contactListLogicController.NavigateDown();
				break;
				case 'popup_moveUp_left_stick_up':
					m_contactListLogicController.NavigateUp();
				break;
				case 'showAll':
					ToggleShowAllMessages();
				break;
				case 'secondaryAction':
					AlternativeAcceptAction();
				break;
				case 'click':
					AcceptAction();
				break;
				case 'OpenPauseMenu':
					ListenerActionConsumer.DontSendReleaseEvent( consumer );
				break;
				case 'cancel':
					Back();
				break;
				case 'popup_moveLeft':
				case 'popup_prior':
				case 'popup_next':
				case 'popup_moveRight':
					SelectOtherTab();
				break;
			}
		}
		return true;
	}

	public function ToggleShowAllMessages()
	{
		if( m_screenType == PhoneScreenType.Unread )
		{
			m_isShowingAllMessages = !( m_isShowingAllMessages );
			FindMessageToSelect();
			m_audioSystem.Play( 'ui_menu_map_pin_created' );
			SetScreenType( m_screenType );
			m_messageToOpenHash = ( ( Int32 )( MessageHash.Invalid ) );
		}
	}

	private function FindMessageToSelect()
	{
		var contact : weak< ContactData >;
		contact = m_contactListLogicController.GetSelectedContactData();
		if( !( contact ) )
		{
			m_messageToOpenHash = ( ( Int32 )( MessageHash.Invalid ) );
		}
		else if( contact.type == MessengerContactType.Fake_ShowAll )
		{
			m_messageToOpenHash = ( ( Int32 )( MessageHash.Fake ) );
		}
		else if( !( m_isShowingAllMessages ) )
		{
			m_messageToOpenHash = m_contactListLogicController.GetContactWithUnreadHash();
		}
		else
		{
			m_messageToOpenHash = m_contactListLogicController.GetSelectedContactHash();
		}
	}

	public function AcceptAction()
	{
		switch( m_screenType )
		{
			case PhoneScreenType.Unread:
				ExecuteAction();
			break;
			case PhoneScreenType.Contacts:
				if( m_threadsVisible )
				{
					ExecuteAction();
				}
				else
				{
					CallContact();
				}
			break;
		}
	}

	public function AlternativeAcceptAction()
	{
		switch( m_screenType )
		{
			case PhoneScreenType.Unread:
				CallContact();
			break;
			case PhoneScreenType.Contacts:
				ExecuteAction();
			break;
		}
	}

	public function RefreshSmsMessager( contactData : weak< ContactData > )
	{
		var evt : RefreshSmsMessagerEvent;
		evt = new RefreshSmsMessagerEvent;
		evt.m_data = new JournalNotificationData;
		evt.m_data.type = contactData.type;
		evt.m_data.contactNameLocKey = StringToName( contactData.localizedName );
		if( contactData.type == MessengerContactType.MultiThread )
		{
			evt.m_data.journalEntry = m_journalMgr.GetEntry( ( ( Uint32 )( contactData.conversationHash ) ) );
		}
		else
		{
			evt.m_data.journalEntry = m_journalMgr.GetEntry( ( ( Uint32 )( contactData.hash ) ) );
		}
		evt.m_data.mode = JournalNotificationMode.HUD;
		evt.m_data.openedFromPhone = true;
		evt.m_data.source = m_screenType;
		QueueEvent( evt );
	}

	public function FocusSmsMessenger()
	{
		if( m_messagesPanelSpawned )
		{
			DisableContactsInput();
			QueueEvent( new FocusSmsMessagerEvent );
		}
	}

	private function MoveMessengerLeft( moveBackToRight : Bool )
	{
		var playbackOptions : inkAnimOptions;
		playbackOptions.playReversed = moveBackToRight;
		PlayLibraryAnimation( 'sms_move_left', playbackOptions );
	}

	protected event OnSmsMessageGotFocus( evt : FocusSmsMessagerEvent )
	{
		contactsElement.widget.SetOpacity( 0.30000001 );
		m_contactListLogicController.ShowInputHints( false );
		m_contactListLogicController.OpenSelectedItem();
		m_audioSystem.Play( 'ui_menu_map_pin_created' );
		MoveMessengerLeft( false );
		ShowSmsMessager( true );
	}

	protected event OnSmsMessageLostFocus( evt : UnfocusSmsMessagerEvent )
	{
		var contact : weak< ContactData >;
		var contactChanged : Bool;
		contactsElement.widget.SetOpacity( 1.0 );
		EnableContactsInput();
		m_audioSystem.Play( 'ui_menu_map_pin_on' );
		MoveMessengerLeft( true );
		ShowSmsMessager( false );
		m_contactListLogicController.ShowInputHints( true );
		contact = m_contactListLogicController.GetSelectedContactData();
		if( contact.type == MessengerContactType.Fake_ShowAll )
		{
			return true;
		}
		contactChanged = RefreshReplies( contact );
		m_contactListLogicController.RefreshSelectedContact();
		if( ( ( ( m_screenType == PhoneScreenType.Unread && contactChanged ) && ( contact.unreadMessages.Size() == 0 ) ) && !( contact.playerCanReply ) ) && !( m_isShowingAllMessages ) )
		{
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, RumblePosition.Left );
			m_contactListLogicController.HideSelectedItem();
			m_audioSystem.Play( 'ui_menu_item_disassemble' );
		}
		else if( m_screenType == PhoneScreenType.Contacts && m_isSingleThread )
		{
			Back();
		}
	}

	protected event OnContactHidden( target : weak< inkWidget > )
	{
		var indexToSelect : Uint32;
		if( m_screenType == PhoneScreenType.Unread )
		{
			indexToSelect = m_contactListLogicController.GetSelectedContactIndex();
			m_indexToSelect = ( ( Uint32 )( Max( ( ( Int32 )( indexToSelect ) ) - 1, 0 ) ) );
			SetScreenType( m_screenType );
			m_indexToSelect = 0;
		}
	}

	public function RefreshReplies( contactData : ContactData ) : Bool
	{
		var messagesReceived : array< weak< JournalEntry > >;
		var playerReplies : array< weak< JournalEntry > >;
		var entry : weak< JournalEntry >;
		var unreadCount : Int32;
		var canReply : Bool;
		if( contactData.type == MessengerContactType.SingleThread )
		{
			entry = m_journalMgr.GetEntry( ( ( Uint32 )( contactData.hash ) ) );
			if( !( entry ) )
			{
				return false;
			}
			m_journalMgr.GetFlattenedMessagesAndChoices( entry, messagesReceived, playerReplies );
		}
		else
		{
			entry = m_journalMgr.GetEntry( ( ( Uint32 )( contactData.conversationHash ) ) );
			if( !( entry ) )
			{
				return false;
			}
			m_journalMgr.GetMessagesAndChoices( entry, messagesReceived, playerReplies );
		}
		unreadCount = contactData.unreadMessages.Size();
		canReply = contactData.playerCanReply;
		MessengerUtils.GetContactMessageData( contactData, m_journalMgr, messagesReceived, playerReplies );
		MessengerUtils.RefreshQuestRelatedStatus( contactData, m_journalMgr, messagesReceived );
		return ( unreadCount != contactData.unreadMessages.Size() ) || canReply != contactData.playerCanReply;
	}

	public function CallContact()
	{
		var contactData : weak< ContactData >;
		if( m_contactListLogicController )
		{
			contactData = m_contactListLogicController.GetSelectedContactData();
			if( ( contactData == NULL ) || !( contactData.isCallable ) )
			{
				return;
			}
			if( !( m_PhoneSystem.IsCallingEnabled() ) )
			{
				ShowActionBlockedNotification();
				return;
			}
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, RumblePosition.Right );
			CallSelectedContact( contactData );
		}
	}

	private function ShowActionBlockedNotification()
	{
		var notificationEvent : UIInGameNotificationEvent;
		notificationEvent = new UIInGameNotificationEvent;
		m_uiSystem.QueueEvent( new UIInGameNotificationRemoveEvent );
		notificationEvent.m_notificationType = UIInGameNotificationType.CombatRestriction;
		m_uiSystem.QueueEvent( notificationEvent );
	}

	public function ExecuteAction()
	{
		var contactData : weak< ContactData >;
		if( m_contactListLogicController )
		{
			contactData = m_contactListLogicController.GetSelectedContactData();
			if( contactData == NULL )
			{
				return;
			}
			if( ( contactData.type == MessengerContactType.Contact && !( contactData.hasMessages ) ) && !( contactData.playerCanReply ) )
			{
				return;
			}
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, RumblePosition.Right );
			switch( contactData.type )
			{
				case MessengerContactType.Contact:
					ShowSelectedContactMessages( contactData );
				break;
				case MessengerContactType.SingleThread:
				case MessengerContactType.MultiThread:
					FocusSmsMessenger();
				break;
				case MessengerContactType.Fake_ShowAll:
					ToggleShowAllMessages();
				break;
				default:
					break;
			}
		}
	}

	public function ShowSelectedContactMessages( contactData : weak< ContactData > )
	{
		var messages : array< IScriptable >;
		m_threadsVisible = true;
		messages = MessengerUtils.GetMessageDataArrayForContact( m_journalMgr, contactData.hash, true, true );
		m_contactListLogicController.PushList( messages, ContactsSortMethod.ByTime );
		m_audioSystem.Play( 'ui_menu_map_pin_created' );
		m_contactListLogicController.SetTitle( contactData.localizedName );
		m_contactListLogicController.ShowTitle( true );
		m_isSingleThread = messages.Size() == 1;
	}

	public function GotoSmsMessenger( contactData : weak< ContactData > )
	{
		var evt : OpenSmsMessengerEvent;
		evt = new OpenSmsMessengerEvent;
		evt.m_data = new JournalNotificationData;
		evt.m_data.type = contactData.type;
		evt.m_data.contactNameLocKey = StringToName( contactData.localizedName );
		if( contactData.type == MessengerContactType.MultiThread )
		{
			evt.m_data.journalEntry = m_journalMgr.GetEntry( ( ( Uint32 )( contactData.conversationHash ) ) );
		}
		else
		{
			evt.m_data.journalEntry = m_journalMgr.GetEntry( ( ( Uint32 )( contactData.hash ) ) );
		}
		evt.m_data.mode = JournalNotificationMode.HUD;
		evt.m_data.openedFromPhone = true;
		evt.m_data.source = m_screenType;
		QueueEvent( evt );
	}

	protected event OnPSMIsControllingDeviceChanged( value : Bool )
	{
		m_isRemoteControllingDevice = value;
		ResolveVisibility();
	}

	protected event OnNotificationsQueueChanged( id : Int32 )
	{
		if( id == GetID() )
		{
			ActivatePhoneElement( gameuiActivePhoneElement.Notifications );
		}
		else if( m_currActiveQueueId = GetID() )
		{
			DeactivatePhoneElement( gameuiActivePhoneElement.Notifications );
		}
		m_currActiveQueueId = id;
	}

	protected event OnPhoneIconSpawned( widget : inkWidget, userData : IScriptable )
	{
		phoneIconElement.widget = widget;
		ResolveVisibility();
	}

	protected event OnConsumableTutorial( value : Int32 )
	{
		ResolveVisibility();
	}

	protected event OnDpadVisibilityChanged( value : Int32 )
	{
		ResolveVisibility();
	}

	protected event OnGameStarted( value : Int32 )
	{
		ResolveVisibility();
	}

	protected event OnMenuUpdate( value : Bool )
	{
		SetCallingPaused( value );
		SetNotificationPaused( value );
		GetRootWidget().SetVisible( !( value ) );
	}

	private function SetCallingPaused( value : Bool )
	{
		var controller : IncomingCallLogicController;
		if( incomingCallElement.widget )
		{
			controller = ( ( IncomingCallLogicController )( incomingCallElement.widget.GetController() ) );
			controller.SetCallingPaused( value );
		}
	}

	protected event OnJournalUpdate( hash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		var entry : weak< JournalEntry >;
		var state : gameJournalEntryState;
		switch( className )
		{
			case 'gameJournalPhoneChoiceEntry':
				entry = m_journalMgr.GetEntry( hash );
			state = m_journalMgr.GetEntryState( entry );
			if( state == gameJournalEntryState.Active )
			{
				NotifyOrRefreshData( entry, state );
			}
			break;
			case 'gameJournalPhoneMessage':
				entry = m_journalMgr.GetEntry( hash );
			state = m_journalMgr.GetEntryState( entry );
			if( ( notifyOption == JournalNotifyOption.Notify && state == gameJournalEntryState.Active ) && changeType == JournalChangeType.Direct )
			{
				NotifyOrRefreshData( entry, state );
			}
			break;
			case 'gameJournalContact':
				entry = m_journalMgr.GetEntry( hash );
			state = m_journalMgr.GetEntryState( entry );
			if( ( notifyOption == JournalNotifyOption.Notify && state == gameJournalEntryState.Active ) && changeType == JournalChangeType.Direct )
			{
				ShowContactUpdate( entry, state );
			}
			break;
			default:
				break;
		}
	}

	private function NotifyOrRefreshData( entry : weak< JournalEntry >, state : gameJournalEntryState )
	{
		if( contactsElement.widget )
		{
			if( m_screenType == PhoneScreenType.Unread && !( m_messagesPanelVisible ) )
			{
				SetScreenType( PhoneScreenType.Unread );
			}
		}
		else
		{
			ShowNewMessage( entry, state );
		}
	}

	protected event OnJournalEntryVisited( hash : Uint32, className : CName, notifyOption : JournalNotifyOption, changeType : JournalChangeType )
	{
		var request : JournalEntryNotificationRemoveRequestData;
		if( ( className == 'gameJournalQuest' || className == 'gameJournalPhoneMessage' ) || className == 'gameJournalContact' )
		{
			request = new JournalEntryNotificationRemoveRequestData;
			request.entryHash = hash;
			RemoveNotification( request );
		}
	}

	public function CreateTriggerCallRequestFromPhoneCallInformation( phoneCallInformation : PhoneCallInformation ) : questTriggerCallRequest
	{
		var request : questTriggerCallRequest;
		request = new questTriggerCallRequest;
		request.callPhase = phoneCallInformation.callPhase;
		request.visuals = phoneCallInformation.visuals;
		request.isRejectable = phoneCallInformation.isRejectable;
		if( phoneCallInformation.isAudioCall )
		{
			request.callMode = questPhoneCallMode.Audio;
		}
		else
		{
			request.callMode = questPhoneCallMode.Video;
		}
		if( phoneCallInformation.isPlayerCalling )
		{
			request.caller = 'player';
			request.addressee = phoneCallInformation.contactName;
		}
		else
		{
			request.caller = phoneCallInformation.contactName;
			request.addressee = 'player';
		}
		return request;
	}

	protected event OnPhoneCall( value : Variant )
	{
		m_CurrentCallInformation = ( ( PhoneCallInformation )value );
		m_CurrentPhoneCallContact = GetIncomingContact();
		if( m_CurrentCallInformation.visuals == questPhoneCallVisuals.Somi )
		{
			return false;
		}
		if( ( ( !( incomingCallElement.request ) && !( incomingCallElement.widget ) ) && m_CurrentCallInformation.callPhase == questPhoneCallPhase.IncomingCall ) && !( m_CurrentCallInformation.isPlayerCalling ) )
		{
			incomingCallElement.request = AsyncSpawnFromLocal( inkCompoundRef.Get( incomingCallElement.slot ), incomingCallElement.libraryID, this, 'OnIncommingCallSpawned' );
		}
		switch( m_CurrentCallInformation.callPhase )
		{
			case questPhoneCallPhase.Undefined:
			case questPhoneCallPhase.EndCall:
				CancelPendingSpawnRequests();
			SetTalkingTrigger( m_CurrentCallInformation.isPlayerCalling, questPhoneTalkingState.Ended );
			SetPhoneFunction( EHudPhoneFunction.Inactive );
			break;
			case questPhoneCallPhase.IncomingCall:
			case questPhoneCallPhase.StartCall:
				HandleCall();
			break;
		}
	}

	private function CancelPendingSpawnRequests()
	{
		if( holoAudioCallElement.request && !( holoAudioCallElement.widget ) )
		{
			holoAudioCallElement.request.Cancel();
		}
	}

	private function HandleCall()
	{
		if( !( holoAudioCallElement.request ) && !( holoAudioCallElement.widget ) )
		{
			holoAudioCallElement.request = AsyncSpawnFromLocal( inkCompoundRef.Get( holoAudioCallElement.slot ), holoAudioCallElement.libraryID, this, 'OnHoloAudioCallSpawned' );
		}
		else
		{
			UpdateHoloAudioCall();
		}
	}

	protected event OnIncommingCallSpawned( widget : inkWidget, userData : IScriptable )
	{
		var controller : weak< IncomingCallLogicController >;
		if( widget )
		{
			if( holoAudioCallElement.widget != NULL )
			{
				m_holoAudioCallLogicController.Hide();
			}
			incomingCallElement.widget = widget;
			incomingCallElement.widget.RegisterToCallback( 'OnIncomingCallFinished', this, 'OnIncomingCallFinished' );
			ActivatePhoneElement( gameuiActivePhoneElement.IncomingCall );
			controller = ( ( IncomingCallLogicController )( incomingCallElement.widget.GetController() ) );
			if( controller )
			{
				controller.SetCallInfo( m_CurrentPhoneCallContact.GetLocalizedName( m_journalMgr ), m_CurrentPhoneCallContact, m_journalMgr, m_CurrentCallInformation.isRejectable );
			}
		}
	}

	public function GetIncomingContact() : weak< JournalContact >
	{
		var i, limit : Int32;
		var currContact : weak< JournalContact >;
		var contactsList : array< weak< JournalEntry > >;
		var context : JournalRequestContext;
		context.stateFilter.active = true;
		context.stateFilter.inactive = true;
		m_journalMgr.GetContacts( context, contactsList );
		for( i = 0, limit = contactsList.Size(); i < limit; i += 1 )
		{
			currContact = ( ( JournalContact )( contactsList[ i ] ) );
			if( currContact.GetId() == NameToString( m_CurrentCallInformation.contactName ) )
			{
				return currContact;
			}
		}
		return NULL;
	}

	protected event OnIncomingCallFinished( target : weak< inkWidget > )
	{
		DeactivatePhoneElement( gameuiActivePhoneElement.IncomingCall );
		inkCompoundRef.RemoveChild( incomingCallElement.slot, incomingCallElement.widget );
		incomingCallElement.widget = NULL;
	}

	protected event OnHoloAudioCallSpawned( widget : inkWidget, userData : IScriptable )
	{
		if( widget )
		{
			holoAudioCallElement.widget = widget;
			holoAudioCallElement.widget.RegisterToCallback( 'OnHoloAudioCallFinished', this, 'OnHoloAudioCallFinished' );
			ActivatePhoneElement( gameuiActivePhoneElement.Call );
			m_holoAudioCallLogicController = ( ( HoloAudioCallLogicController )( holoAudioCallElement.widget.GetController() ) );
			UpdateHoloAudioCall();
		}
	}

	private function UpdateHoloAudioCall()
	{
		if( m_holoAudioCallLogicController )
		{
			CancelTimeoutFailsafe();
			switch( m_CurrentCallInformation.callPhase )
			{
				case questPhoneCallPhase.Undefined:
				case questPhoneCallPhase.EndCall:
					break;
				case questPhoneCallPhase.IncomingCall:
					PlaySound( 'PhoneCallPopup', 'OnOpen' );
				StartTimeoutFailsafe();
				SetPhoneFunction( EHudPhoneFunction.IncomingCall );
				if( m_CurrentCallInformation.isPlayerCalling )
				{
					m_holoAudioCallLogicController.Show();
				}
				break;
				case questPhoneCallPhase.StartCall:
					SetTalkingTrigger( m_CurrentCallInformation.isPlayerCalling, questPhoneTalkingState.Talking );
				SetPhoneFunction( ( ( m_CurrentCallInformation.isAudioCall ) ? ( EHudPhoneFunction.Audiocall ) : ( EHudPhoneFunction.Holocall ) ) );
				OnIncomingCallFinished( NULL );
				m_holoAudioCallLogicController.Show();
				break;
			}
		}
	}

	protected event OnHoloAudioCallFinished( target : weak< inkWidget > )
	{
		DeactivatePhoneElement( gameuiActivePhoneElement.Call );
		m_holoAudioCallLogicController = NULL;
		inkCompoundRef.RemoveChild( holoAudioCallElement.slot, holoAudioCallElement.widget );
		holoAudioCallElement.widget = NULL;
	}

	protected event OnPhoneStatusChanged( phoneStatus : CName )
	{
		if( m_holoAudioCallLogicController )
		{
			m_holoAudioCallLogicController.SetStatusText( NameToString( phoneStatus ) );
		}
	}

	protected event OnPhoneMinimized( value : Bool )
	{
		if( m_holoAudioCallLogicController )
		{
			m_holoAudioCallLogicController.ChangeMinimized( value );
		}
	}

	public function StartTimeoutFailsafe()
	{
		var timeoutRequest : PhoneTimeoutRequest;
		timeoutRequest = new PhoneTimeoutRequest;
		if( m_DelaySystem )
		{
			m_DelayedTimeoutCallbackId = m_DelaySystem.DelayScriptableSystemRequest( 'PhoneSystem', timeoutRequest, m_TimeoutPeroid );
		}
	}

	public function CancelTimeoutFailsafe()
	{
		if( m_DelaySystem )
		{
			m_DelaySystem.CancelCallback( m_DelayedTimeoutCallbackId );
		}
	}

	public function SetTalkingTrigger( isPlayerCalling : Bool, state : questPhoneTalkingState )
	{
		var request : TalkingTriggerRequest;
		if( m_CurrentPhoneCallContact )
		{
			request = new TalkingTriggerRequest;
			request.isPlayerCalling = isPlayerCalling;
			request.contact = StringToName( m_CurrentPhoneCallContact.GetId() );
			request.state = state;
			m_PhoneSystem.QueueRequest( request );
		}
	}

	public function SetPhoneFunction( newFunction : EHudPhoneFunction )
	{
		var avatarID : TweakDBID;
		var contactName : String;
		if( m_holoAudioCallLogicController && m_CurrentFunction != newFunction )
		{
			m_CurrentFunction = newFunction;
			avatarID = m_CurrentPhoneCallContact.GetAvatarID( m_journalMgr );
			contactName = m_CurrentPhoneCallContact.GetLocalizedName( m_journalMgr );
			switch( newFunction )
			{
				case EHudPhoneFunction.DisplayingMessage:
					break;
				case EHudPhoneFunction.IncomingCall:
					m_holoAudioCallLogicController.ShowIncomingContact( avatarID, contactName );
				break;
				case EHudPhoneFunction.Audiocall:
					m_holoAudioCallLogicController.StartAudiocall( avatarID, contactName, m_CurrentCallInformation.showAvatar );
				break;
				case EHudPhoneFunction.Holocall:
					m_holoAudioCallLogicController.StartHolocall( avatarID, contactName );
				break;
				case EHudPhoneFunction.Inactive:
					m_holoAudioCallLogicController.ShowEndCallContact( avatarID, contactName );
				break;
			}
		}
		if( incomingCallElement.widget != NULL )
		{
			if( newFunction == EHudPhoneFunction.Inactive )
			{
				OnIncomingCallFinished( NULL );
			}
		}
	}

	public function CachePredefinedRestrictions()
	{
		PlayerGameplayRestrictions.AcquireHotkeyRestrictionTags( EHotkey.DPAD_DOWN, m_gameplayRestrictions );
	}

	protected event OnTimeSkip( evt : TimeSkipFinishEvent )
	{
		if( IsPhoneActive() )
		{
			CloseContactList();
		}
	}

	protected event OnKeepPhoneOpenWhenInHubMenu( evt : KeepPhoneOpenWhenInHubMenuEvent )
	{
		m_keepOpenWhenInHubMenu = true;
	}

	protected event OnHUBMenuChanged( evt : inkMenuLayer_SetMenuModeEvent )
	{
		var mode : inkMenuMode;
		var state : inkMenuState;
		mode = evt.GetMenuMode();
		state = evt.GetMenuState();
		if( !( IsPhoneActive() ) || mode != inkMenuMode.HubMenu )
		{
			return false;
		}
		if( state == inkMenuState.Enabled && !( m_keepOpenWhenInHubMenu ) )
		{
			CloseContactList();
		}
		else
		{
			m_keepOpenWhenInHubMenu = m_bbUiSystem.GetBool( m_bbUiSystemDef.IsInMenu );
		}
	}

	protected event OnContactsActive( value : Bool )
	{
		if( value && !( contactsElement.widget ) )
		{
			if( !( contactsElement.request ) )
			{
				contactsElement.request = AsyncSpawnFromLocal( inkCompoundRef.Get( contactsElement.slot ), contactsElement.libraryID, this, 'OnContactListSpawned' );
			}
		}
		else if( !( value ) && contactsElement.widget )
		{
			OnContactListClosed();
		}
		else if( !( value ) && contactsElement.request )
		{
			contactsElement.request.Cancel();
		}
	}

	protected event OnContactListSpawned( widget : inkWidget, userData : IScriptable )
	{
		var contactDataArray : array< IScriptable >;
		var hasImportantCalls : Bool;
		var hasImportantMessages : Bool;
		var hasUnreadMsg : Bool;
		var isCallingEnabled : Bool;
		if( widget )
		{
			contactsElement.widget = widget;
			contactsElement.widget.RegisterToCallback( 'OnCloseContactList', this, 'OnCloseContactList' );
			contactsElement.widget.RegisterToCallback( 'OnContactHidden', this, 'OnContactHidden' );
			ActivatePhoneElement( gameuiActivePhoneElement.Contacts );
			m_contactListLogicController = ( ( PhoneDialerLogicController )( contactsElement.widget.GetController() ) );
			if( m_contactListLogicController )
			{
				m_uiSystem.PushGameContext( UIGameContext.ModalPopup );
				m_uiSystem.RequestNewVisualState( 'inkModalPopupState' );
				TimeDilationHelper.SetTimeDilationWithProfile( m_player, "radialMenu", true, true );
				PopupStateUtils.SetBackgroundBlur( this, true );
				isCallingEnabled = m_PhoneSystem.IsCallingEnabled();
				hasImportantCalls = MessengerUtils.HasQuestImportantCalls( m_journalMgr );
				hasImportantMessages = MessengerUtils.HasQuestImportantMessages( m_journalMgr );
				contactDataArray = MessengerUtils.GetSimpleContactDataArray( m_journalMgr, true, true, m_isShowingAllMessages );
				hasUnreadMsg = contactDataArray.Size() > 0;
				m_contactListLogicController.ShowCallsQuestIndicator( hasImportantCalls );
				m_contactListLogicController.SetCallingEnabled( isCallingEnabled );
				VerifyMessageToOpenHash( contactDataArray, m_messageToOpenHash );
				if( m_messageToOpenHash != ( ( Int32 )( MessageHash.Invalid ) ) )
				{
					SetScreenType( PhoneScreenType.Unread );
				}
				else if( hasImportantMessages )
				{
					SetScreenType( PhoneScreenType.Unread );
				}
				else if( hasImportantCalls && isCallingEnabled )
				{
					SetScreenType( PhoneScreenType.Contacts );
				}
				else if( hasUnreadMsg )
				{
					SetScreenType( PhoneScreenType.Unread );
				}
				else
				{
					SetScreenType( PhoneScreenType.Contacts );
				}
				m_audioSystem.Play( 'ui_phone_incoming_call_positive' );
				m_contactListLogicController.Show();
				PlayRumble( RumbleStrength.SuperLight, RumbleType.Slow, RumblePosition.Left );
				EnableContactsInput();
			}
		}
		if( m_holoAudioCallLogicController )
		{
			m_holoAudioCallLogicController.Interrupt( true );
		}
	}

	private function VerifyMessageToOpenHash( const contactDataArray : ref< array< IScriptable > >, out messageToOpenHash : Int32 )
	{
		var foundInUnread : Bool;
		if( messageToOpenHash != ( ( Int32 )( MessageHash.Invalid ) ) )
		{
			foundInUnread = ContactDataHelper.Contains( contactDataArray, messageToOpenHash );
			if( !( foundInUnread ) )
			{
				messageToOpenHash = ( ( Int32 )( MessageHash.Invalid ) );
			}
		}
	}

	protected event OnMessageToOpenHashChanged( hash : Int32 )
	{
		m_messageToOpenHash = hash;
	}

	protected event OnContactSelectionChanged( evt : ContactSelectionChangedEvent )
	{
		if( evt.ContactData == NULL )
		{
			ShowSmsMessager( false );
			return true;
		}
		if( ( m_screenType == PhoneScreenType.Contacts && m_threadsVisible ) || m_screenType == PhoneScreenType.Unread )
		{
			if( m_messagesPanelSpawned )
			{
				RefreshSmsMessager( evt.ContactData );
				if( m_screenType == PhoneScreenType.Contacts && m_isSingleThread )
				{
					FocusSmsMessenger();
				}
			}
			else
			{
				GotoSmsMessenger( evt.ContactData );
			}
		}
	}

	public function SelectOtherTab()
	{
		var nextScreen : PhoneScreenType;
		nextScreen = GetOtherScreenType( m_screenType );
		PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, RumblePosition.Right );
		m_audioSystem.Play( 'ui_gui_tab_change' );
		if( m_threadsVisible )
		{
			m_threadsVisible = false;
			m_contactListLogicController.PopList();
			m_contactListLogicController.ShowTitle( false );
		}
		if( m_screenType != nextScreen )
		{
			SetScreenType( nextScreen );
		}
	}

	public function GetOtherScreenType( current : PhoneScreenType ) : PhoneScreenType
	{
		return ( ( current == PhoneScreenType.Unread ) ? ( PhoneScreenType.Contacts ) : ( PhoneScreenType.Unread ) );
	}

	public function SetScreenType( type : PhoneScreenType )
	{
		var messages : array< IScriptable >;
		var dots : ContactData;
		m_screenType = type;
		switch( m_screenType )
		{
			case PhoneScreenType.Unread:
				messages = MessengerUtils.GetSimpleContactDataArray( m_journalMgr, true, true, m_isShowingAllMessages );
			dots = CreateFakeContactData( messages.Size() );
			messages.PushBack( dots );
			m_contactListLogicController.UpdateShowAllButton( m_isShowingAllMessages );
			m_contactListLogicController.SetSortMethod( ContactsSortMethod.ByTime );
			m_contactListLogicController.PopulateListData( messages, m_indexToSelect, m_messageToOpenHash );
			m_contactListLogicController.SwtichTabs( PhoneDialerTabs.Unread );
			HideThreads();
			break;
			case PhoneScreenType.Contacts:
				messages = MessengerUtils.GetCallableAndNonEmptyContacts( m_journalMgr );
			m_contactListLogicController.SetSortMethod( ContactsSortMethod.ByName );
			m_contactListLogicController.PopulateListData( messages );
			m_contactListLogicController.SwtichTabs( PhoneDialerTabs.Contacts );
			break;
		}
	}

	private function CreateFakeContactData( messagesCount : Int32 ) : ContactData
	{
		var contact : ContactData;
		contact = new ContactData;
		contact.type = MessengerContactType.Fake_ShowAll;
		contact.id = "fake_show_all";
		contact.hash = ( ( Int32 )( MessageHash.Fake ) );
		if( messagesCount == 0 )
		{
			contact.localizedName = "LocKey#93930";
		}
		else
		{
			contact.localizedName = ( ( m_isShowingAllMessages ) ? ( "LocKey#93929" ) : ( "" ) );
		}
		return contact;
	}

	private function ShowSmsMessager( visible : Bool )
	{
		if( visible == m_messagesPanelVisible )
		{
			return;
		}
		if( visible )
		{
			QueueEvent( new ShowSmsMessagerEvent );
			m_messagesPanelVisible = true;
		}
		else
		{
			QueueEvent( new HideSmsMessagerEvent );
			m_messagesPanelVisible = false;
		}
	}

	protected event OnContactListClosed()
	{
		if( m_contactListLogicController )
		{
			DisableContactsInput();
			m_messagesPanelVisible = false;
			m_messagesPanelSpawned = false;
			m_threadsVisible = false;
			m_keepOpenWhenInHubMenu = false;
			m_repeatingScrollActionEnabled = false;
			m_audioSystem.Play( 'ui_phone_incoming_call_negative' );
			m_contactListLogicController.Hide();
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Fast, RumblePosition.Left );
			TimeDilationHelper.SetTimeDilationWithProfile( m_player, "radialMenu", false, false );
			PopupStateUtils.SetBackgroundBlur( this, false );
			m_uiSystem.PopGameContext( UIGameContext.ModalPopup );
			m_uiSystem.RestorePreviousVisualState( 'inkModalPopupState' );
			DeactivatePhoneElement( gameuiActivePhoneElement.Contacts );
			m_contactListLogicController = NULL;
			inkCompoundRef.RemoveChild( contactsElement.slot, contactsElement.widget );
			contactsElement.widget = NULL;
			CloseSmsMessenger();
		}
		if( m_holoAudioCallLogicController )
		{
			m_holoAudioCallLogicController.Interrupt( false );
		}
	}

	public function CallSelectedContact( contactData : weak< ContactData > )
	{
		var callRequest : questTriggerCallRequest;
		callRequest = new questTriggerCallRequest;
		callRequest.addressee = StringToName( contactData.contactId );
		callRequest.caller = 'Player';
		callRequest.callPhase = questPhoneCallPhase.IncomingCall;
		callRequest.callMode = questPhoneCallMode.Video;
		m_PhoneSystem.QueueRequest( callRequest );
	}

	protected event OnCloseContactList( target : weak< inkWidget > )
	{
		CloseContactList();
	}

	public function CloseContactList()
	{
		if( m_bbUiComDevice )
		{
			m_bbUiComDevice.SetBool( m_bbUiComDeviceDef.ContactsActive, false, true );
		}
	}

	public function Back()
	{
		if( m_threadsVisible )
		{
			m_threadsVisible = false;
			m_contactListLogicController.PopList();
			PlayRumble( RumbleStrength.SuperLight, RumbleType.Pulse, RumblePosition.Right );
			m_audioSystem.Play( 'ui_menu_map_pin_on' );
			m_contactListLogicController.ShowTitle( false );
		}
		else
		{
			CloseContactList();
		}
	}

	public function HideThreads()
	{
		if( m_threadsVisible )
		{
			m_threadsVisible = false;
			m_contactListLogicController.ShowTitle( false );
		}
	}

	protected event OnOpenSmsMessenger( evt : OpenSmsMessengerEvent )
	{
		OpenSmsMessenger( evt.m_data );
	}

	protected event OnSmsMessangerInitalized( e : SmsMessangerInitalizedEvent )
	{
		m_messagesPanelSpawned = true;
		if( m_messageToOpenHash != 0 )
		{
			FocusSmsMessenger();
			m_messageToOpenHash = 0;
		}
	}

	protected event OnCloseSmsMessenger( evt : CloseSmsMessengerEvent )
	{
		CloseSmsMessenger();
		m_messagesPanelSpawned = false;
		m_messagesPanelVisible = false;
	}

	public function EnableContactsInput()
	{
		m_player.RegisterInputListener( this, 'popup_moveDown' );
		m_player.RegisterInputListener( this, 'popup_moveUp' );
		m_player.RegisterInputListener( this, 'popup_moveUp_left_stick_up' );
		m_player.RegisterInputListener( this, 'popup_moveUp_left_stick_down' );
		m_player.RegisterInputListener( this, 'popup_goto_messenger' );
		m_player.RegisterInputListener( this, 'OpenPauseMenu' );
		m_player.RegisterInputListener( this, 'click' );
		m_player.RegisterInputListener( this, 'secondaryAction' );
		m_player.RegisterInputListener( this, 'showAll' );
		m_player.RegisterInputListener( this, 'cancel' );
		m_player.RegisterInputListener( this, 'popup_prior' );
		m_player.RegisterInputListener( this, 'popup_next' );
		m_player.RegisterInputListener( this, 'popup_moveLeft' );
		m_player.RegisterInputListener( this, 'popup_moveRight' );
	}

	public function DisableContactsInput()
	{
		m_player.UnregisterInputListener( this, 'popup_moveDown' );
		m_player.UnregisterInputListener( this, 'popup_moveUp' );
		m_player.UnregisterInputListener( this, 'popup_moveUp_left_stick_up' );
		m_player.UnregisterInputListener( this, 'popup_moveUp_left_stick_down' );
		m_player.UnregisterInputListener( this, 'popup_goto_messenger' );
		m_player.UnregisterInputListener( this, 'OpenPauseMenu' );
		m_player.UnregisterInputListener( this, 'click' );
		m_player.UnregisterInputListener( this, 'secondaryAction' );
		m_player.UnregisterInputListener( this, 'showAll' );
		m_player.UnregisterInputListener( this, 'cancel' );
		m_player.UnregisterInputListener( this, 'popup_prior' );
		m_player.UnregisterInputListener( this, 'popup_next' );
		m_player.UnregisterInputListener( this, 'popup_moveLeft' );
		m_player.UnregisterInputListener( this, 'popup_moveRight' );
	}

	public function ShowContactUpdate( entry : weak< JournalEntry >, state : gameJournalEntryState )
	{
		var contactEntry : weak< JournalContact >;
		var title : String;
		title = GetLocalizedText( "Story-base-gameplay-gui-widgets-notifications-quest_update-_localizationString6" );
		contactEntry = ( ( JournalContact )( entry ) );
		PushNewContactNotification( title, contactEntry.GetLocalizedName( m_journalMgr ), 'notification_newcontact', 'notification_newContactAdded' );
	}

	public function ShowNewMessage( entry : weak< JournalEntry >, state : gameJournalEntryState )
	{
		var msgEntry : weak< JournalPhoneMessage >;
		var msgConversation : weak< JournalPhoneConversation >;
		var action : OpenPhoneMessageAction;
		msgEntry = ( ( JournalPhoneMessage )( entry ) );
		if( msgEntry.GetSender() != gameMessageSender.Player )
		{
			msgConversation = ( ( JournalPhoneConversation )( m_journalMgr.GetParentEntry( msgEntry ) ) );
			if( msgConversation )
			{
				action = new OpenPhoneMessageAction;
				action.m_phoneSystem = m_PhoneSystem;
				action.m_journalEntry = msgConversation;
				PushSMSNotification( msgEntry, action );
			}
		}
	}

	public function PushNewContactNotification( const title : ref< String >, const text : ref< String >, widget : CName, animation : CName, optional action : GenericNotificationBaseAction )
	{
		var notificationData : gameuiGenericNotificationData;
		var userData : QuestUpdateNotificationViewData;
		userData = new QuestUpdateNotificationViewData;
		userData.title = title;
		userData.text = text;
		userData.action = action;
		userData.animation = animation;
		userData.soundEvent = 'QuestUpdatePopup';
		userData.soundAction = 'OnOpen';
		notificationData.time = 6.0;
		notificationData.widgetLibraryItemName = widget;
		notificationData.notificationData = userData;
		AddNewNotificationData( notificationData );
	}

	public function PushSMSNotification( msgEntry : weak< JournalPhoneMessage >, optional action : GenericNotificationBaseAction )
	{
		var msgConversation : weak< JournalPhoneConversation >;
		var msgContact : weak< JournalContact >;
		var notificationData : gameuiGenericNotificationData;
		var userData : PhoneMessageNotificationViewData;
		msgConversation = ( ( JournalPhoneConversation )( m_journalMgr.GetParentEntry( msgEntry ) ) );
		msgContact = ( ( JournalContact )( m_journalMgr.GetParentEntry( msgConversation ) ) );
		userData = new PhoneMessageNotificationViewData;
		userData.entryHash = m_journalMgr.GetEntryHash( msgEntry );
		userData.threadHash = m_journalMgr.GetEntryHash( msgConversation );
		userData.contactHash = m_journalMgr.GetEntryHash( msgContact );
		userData.title = msgContact.GetLocalizedName( m_journalMgr );
		userData.SMSLocKey = msgEntry.GetText();
		userData.SMSText = GetLocalizedText( msgEntry.GetText() );
		userData.action = action;
		userData.animation = 'notification_phone_MSG';
		userData.soundEvent = 'PhoneSmsPopup';
		userData.soundAction = 'OnOpen';
		notificationData.time = 6.69999981;
		notificationData.widgetLibraryItemName = 'notification_message';
		notificationData.notificationData = userData;
		AddNewNotificationData( notificationData );
	}

	private function ResolveVisibility()
	{
		if( !( phoneIconElement.widget ) )
		{
			return;
		}
		if( GameStarted() )
		{
			phoneIconElement.widget.SetVisible( !( m_isRemoteControllingDevice ) && TutorialActivated() );
		}
		else
		{
			if( !( TutorialActivated() ) )
			{
				phoneIconElement.widget.SetVisible( true );
			}
			else if( IsVisibilityForced() )
			{
				phoneIconElement.widget.SetVisible( true );
			}
		}
	}

	private function IsVisibilityForced() : Bool
	{
		if( m_questsSystem )
		{
			return m_questsSystem.GetFact( 'dpad_hint_phone_visible' );
		}
		return false;
	}

	private function GameStarted() : Bool
	{
		if( m_questsSystem )
		{
			return m_questsSystem.GetFact( 'q000_started' );
		}
		return false;
	}

	private function TutorialActivated() : Bool
	{
		if( m_questsSystem )
		{
			return m_questsSystem.GetFact( 'dpad_hints_visibility_enabled' );
		}
		return false;
	}

	private function ActivatePhoneElement( element : gameuiActivePhoneElement )
	{
		if( !( TestPhoneElement( element ) ) )
		{
			if( !( AnyElementExceptInVehicle() ) && element != gameuiActivePhoneElement.InVehicle )
			{
				if( phoneIconElement.widget )
				{
					phoneIconElement.widget.CallCustomCallback( 'OnPhoneDeviceSlot' );
				}
			}
			m_activePhoneElements = m_activePhoneElements | ( ( Uint32 )( element ) );
			m_bbUiComDevice.SetUint( m_bbUiComDeviceDef.ActivatePhoneElements, m_activePhoneElements );
			PlayPhoneIconAnim( element );
			PlayBackgroundAnim( element );
		}
	}

	private function DeactivatePhoneElement( element : gameuiActivePhoneElement )
	{
		var notElement : Uint32;
		if( TestPhoneElement( element ) )
		{
			notElement = ~( ( ( Uint32 )( element ) ) );
			m_activePhoneElements = m_activePhoneElements & notElement;
			m_bbUiComDevice.SetUint( m_bbUiComDeviceDef.ActivatePhoneElements, m_activePhoneElements );
			if( !( AnyElementExceptInVehicle() ) )
			{
				if( phoneIconElement.widget )
				{
					phoneIconElement.widget.CallCustomCallback( 'OnPhoneDeviceReset' );
				}
			}
			PlayPhoneIconAnim( element, true );
			PlayBackgroundAnim( element, true );
		}
	}

	private function AnyElementExceptInVehicle() : Bool
	{
		return m_activePhoneElements & ~( ( ( Uint32 )( gameuiActivePhoneElement.InVehicle ) ) );
	}

	private function TestPhoneElement( element : gameuiActivePhoneElement ) : Bool
	{
		return ( m_activePhoneElements & ( ( Uint32 )( element ) ) ) != ( ( Uint32 )( 0 ) );
	}

	private function PlayPhoneIconAnim( element : gameuiActivePhoneElement, optional deactivation : Bool )
	{
		var i, count : Int32;
		var value : gameuiActivePhoneElement;
		count = ( ( Int32 )( gameuiActivePhoneElement.None ) );
		for( i = 0; i < count; i = i + 1 )
		{
			value = ( ( gameuiActivePhoneElement )( i ) );
			if( i < ( ( Int32 )( element ) ) )
			{
				if( TestPhoneElement( value ) )
				{
					return;
				}
			}
			else
			{
				if( !( deactivation ) )
				{
					break;
				}
				else if( TestPhoneElement( value ) )
				{
					break;
				}
			}
		}
		if( i == count )
		{
			value = gameuiActivePhoneElement.None;
		}
		if( m_phoneIconAnimProxy )
		{
			m_phoneIconAnimProxy.Stop();
			m_phoneIconAnimProxy = NULL;
		}
		switch( value )
		{
			case gameuiActivePhoneElement.Call:
			case gameuiActivePhoneElement.IncomingCall:
				m_phoneIconAnimProxy = PlayLibraryAnimation( '2Call' );
			break;
			case gameuiActivePhoneElement.Contacts:
			case gameuiActivePhoneElement.SmsMessenger:
				m_phoneIconAnimProxy = PlayLibraryAnimation( '2Contacts' );
			break;
			case gameuiActivePhoneElement.Notifications:
				m_phoneIconAnimProxy = PlayLibraryAnimation( '2Notifications' );
			break;
			case gameuiActivePhoneElement.InVehicle:
				m_phoneIconAnimProxy = PlayLibraryAnimation( '2Vehicle' );
			break;
			case gameuiActivePhoneElement.None:
				m_phoneIconAnimProxy = PlayLibraryAnimation( '2Phone' );
			break;
		}
	}

	private function PlayBackgroundAnim( element : gameuiActivePhoneElement, optional deactivation : Bool )
	{
		if( element == gameuiActivePhoneElement.Contacts || element == gameuiActivePhoneElement.SmsMessenger )
		{
			if( m_backgroundAnimProxy )
			{
				m_backgroundAnimProxy.Stop();
				m_backgroundAnimProxy = NULL;
			}
			if( deactivation )
			{
				m_backgroundAnimProxy = PlayLibraryAnimation( 'bg_hide' );
			}
			else
			{
				m_backgroundAnimProxy = PlayLibraryAnimation( 'bg_show' );
			}
		}
	}

	protected event OnResolutionChanged()
	{
		if( m_activePhoneElements == 0 )
		{
			m_phoneIconAnimProxy.Stop();
			m_phoneIconAnimProxy = NULL;
			return true;
		}
		return false;
	}

	public export function StopPhoneIconAnim()
	{
		if( m_phoneIconAnimProxy && m_phoneIconAnimProxy.IsPlaying() )
		{
			m_phoneIconAnimProxy.Stop();
			m_phoneIconAnimProxy = NULL;
		}
	}

	public export function GetTopmostActivePhoneElement() : gameuiActivePhoneElement
	{
		var i, count : Int32;
		var value : gameuiActivePhoneElement;
		count = ( ( Int32 )( gameuiActivePhoneElement.None ) );
		for( i = 0; i < count; i = i + 1 )
		{
			value = ( ( gameuiActivePhoneElement )( i ) );
			if( TestPhoneElement( value ) )
			{
				return value;
			}
		}
		return gameuiActivePhoneElement.None;
	}

}

