From 9cab94f2530f8bf60ecb7e607efa93a2960c9528 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Mon, 9 Dec 2024 09:48:14 -0500 Subject: [PATCH] Make game unfocused on incoming call --- osu.Framework.iOS/IOSCallObserver.cs | 44 ++++++++++++++++++++++++++ osu.Framework.iOS/IOSWindow.cs | 46 ++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 osu.Framework.iOS/IOSCallObserver.cs diff --git a/osu.Framework.iOS/IOSCallObserver.cs b/osu.Framework.iOS/IOSCallObserver.cs new file mode 100644 index 00000000000..4c365a173f4 --- /dev/null +++ b/osu.Framework.iOS/IOSCallObserver.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using CallKit; +using Foundation; + +namespace osu.Framework.iOS +{ + internal class IOSCallObserver : NSObject, ICXCallObserverDelegate + { + private readonly Action onCall; + private readonly Action onCallEnded; + + private readonly CXCallController callController; + + public IOSCallObserver(Action onCall, Action onCallEnded) + { + this.onCall = onCall; + this.onCallEnded = onCallEnded; + + callController = new CXCallController(); + callController.CallObserver.SetDelegate(this, null); + + if (callController.CallObserver.Calls.Any(c => !c.HasEnded)) + onCall(); + } + + public void CallChanged(CXCallObserver callObserver, CXCall call) + { + if (!call.HasEnded) + onCall(); + else + onCallEnded(); + } + + protected override void Dispose(bool disposing) + { + callController.Dispose(); + base.Dispose(disposing); + } + } +} diff --git a/osu.Framework.iOS/IOSWindow.cs b/osu.Framework.iOS/IOSWindow.cs index 012092f2240..f5b3512b702 100644 --- a/osu.Framework.iOS/IOSWindow.cs +++ b/osu.Framework.iOS/IOSWindow.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Platform.SDL3; +using SDL; using static SDL.SDL3; using UIKit; @@ -22,6 +23,8 @@ internal class IOSWindow : SDL3MobileWindow public UIWindow UIWindow => uiWindow!; + private IOSCallObserver callObserver; + public override Size Size { get => base.Size; @@ -50,8 +53,51 @@ public override void Create() var appDelegate = (GameApplicationDelegate)UIApplication.SharedApplication.Delegate; appDelegate.DragDrop += TriggerDragDrop; + + // osu! cannot operate when a call takes place, as the audio is completely cut from the game, making it behave in unexpected manner. + // while this is o!f code, it's simpler to do this here rather than in osu!. + // we can reconsider this if there are framework consumers which find this behaviour undesirable. + callObserver = new IOSCallObserver(onCall, onCallEnded); + updateFocused(); } + private bool appInForeground; + private bool inCall; + + protected override void HandleEvent(SDL_Event e) + { + switch (e.Type) + { + case SDL_EventType.SDL_EVENT_WINDOW_MINIMIZED: + case SDL_EventType.SDL_EVENT_WINDOW_FOCUS_LOST: + appInForeground = false; + updateFocused(); + break; + + case SDL_EventType.SDL_EVENT_WINDOW_RESTORED: + case SDL_EventType.SDL_EVENT_WINDOW_FOCUS_GAINED: + appInForeground = true; + updateFocused(); + break; + } + + base.HandleEvent(e); + } + + private void onCall() + { + inCall = true; + updateFocused(); + } + + private void onCallEnded() + { + inCall = false; + updateFocused(); + } + + private void updateFocused() => Focused = appInForeground && !inCall; + protected override unsafe void RunMainLoop() { // Delegate running the main loop to CADisplayLink.