How to detect the media remote on Xbox with UWP

How to detect the media remote on Xbox with UWP

If you write an application on Xbox One with UWP, you are forced to support 2 types of controllers: gamepad and media remote.

Unfortunately, a lot of applications don’t fully support the second one with 2 common mistakes:
– Some features aren’t accessible with the media remote cause using a button not available (for example, a feature only accessible if the user presses trigger R or Y)
– UI isn’t updated to display buttons of the media remote.

We will focus on this blog post, let’s take this example (in the Dropbox app):

If the user uses a media remote with this application instead of a gamepad, he/she will have some troubles to understand how he/she can access to these feature cause the “X” key isn’t available on a media remote. Very frustrating for the user, especially because the media remote is the perfect controller for app navigation.

To solve this issue, I wrote an helper detecting automatically if the user uses a gamepad or a media remote, so developers will be able to adapt their UI/UX to the used input device.

How to detect if the controller used is a media remote?

This is the tricky part cause UWP has no API to detect the type of the controller. But I found an interesting way to do it.

It looks like that ALL media remotes have exactly the same device id: GIP:0000F50000000001. If we think about it, it makes sense, cause contrary to gamepads, you don’t need to pair them to use them on your console, they directly accept all events coming from input devices with this ID.

But how do I know if the user currently holds a gamepad or a remote?

We will rely on KeyDown events for that. Every times the user will press a key (D-pad, A, B, X, Y, etc…) we will check the device id of the sender and guess if it’s a remote or not and. For the first version of our module, we will assume that all other device ids are gamepads.

private static GameControllerType GetGameControllerType(string device_id)
{
   return deviceId == "GIP:0000F50000000001"? GameControllerType.MEDIAREMOTE: GameControllerType.GAMEPAD;
}

 

Retrieve the device id

KeyRoutedEventArgs and KeyEventArgs contain a property DeviceId, but we need to be careful cause this property has been added in Windows 10 Anniversary Edition. Even if UWP wasn’t available prior to this version on Xbox, we should keep in mind that our application would probably support prior versions of Windows on other platforms (mobile, desktop, etc…).

private static string GetDeviceId(KeyRoutedEventArgs arg)
{
    return ApiInformation.IsPropertyPresent("Windows.UI.Xaml.Input.KeyRoutedEventArgs", "DeviceId") ? arg.DeviceId : null;
}

private static string GetDeviceId(KeyEventArgs arg)
{
    return ApiInformation.IsPropertyPresent("Windows.UI.Core.KeyEventArgs", "DeviceId") ? arg.DeviceId : null;
}

Only detect on Xbox

Gamepads are compatible with Windows 10 desktop, Mixed reality, etc… but not media remote, so it’s useless to try to detect if an user uses it on other platforms.

 _isEnable = AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Xbox";

How to use GameControllerDetector in your application?

First, you need to call RegisterCurrentWindow for each window of your application (usually only one on Xbox), you can do it in App.Launched for example, but be careful to not call it in the App constructor cause the window is not created at this time.

GameControllerDetector.Instance.RegisterCurrentWindow();

Then you use the event GameControllerDetector.Instance.GameControllerChanged or bind GameControllerDetector.Instance.CurrentControllerType to adapt your UI to the input device.

What if one of my control handle key events?

If your control catches key events and set e.Handled to True, the window won’t receive key events and won’t be able to detect the type of the input device.

In this particular case, you must manually call GameControllerDetector.Instance.AnalyzeKeyEvent(…) and pass the key event.

Source and sample app

The full source code and a sample app is available here: https://github.com/rudyhuyn/GameControllerDetector

using System;
using System.ComponentModel;
using Windows.Foundation.Metadata;
using Windows.System.Profile;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;

namespace Huyn.Utils
{
    public class GameControllerDetector : INotifyPropertyChanged
    {
        #region Singleton  
        public static GameControllerDetector Instance { get; }

        static GameControllerDetector()
        {
            Instance = new GameControllerDetector();
        }
        #endregion

        public enum GameControllerType { GAMEPAD, MEDIAREMOTE};
        public event EventHandler<GameControllerType> GameControllerChanged;
        public GameControllerType CurrentControllerType { get; private set; }

        private readonly bool _isEnable;

        private GameControllerDetector()
        {
            _isEnable = true;// AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Xbox";
        }

        private static string GetDeviceId(KeyRoutedEventArgs arg)
        {
            return ApiInformation.IsPropertyPresent("Windows.UI.Xaml.Input.KeyRoutedEventArgs", "DeviceId") ? arg.DeviceId : null;
        }

        private static string GetDeviceId(KeyEventArgs arg)
        {
            return ApiInformation.IsPropertyPresent("Windows.UI.Core.KeyEventArgs", "DeviceId") ? arg.DeviceId : null;
        }

        private static GameControllerType GetGameControllerType(string deviceId)
        {
            return deviceId == "GIP:0000F50000000001"? GameControllerType.MEDIAREMOTE: GameControllerType.GAMEPAD;
        }

        public void AnalyzeKeyEvent(KeyRoutedEventArgs arg)
        {
            SetCurrentDeviceId(GetDeviceId(arg));
        }

        internal void RegisterCurrentWindow()
        {
            if (!_isEnable)
                return;
            var coreWindow = CoreWindow.GetForCurrentThread();
            if (coreWindow == null)
                return;

            coreWindow.KeyDown -= CoreWindow_KeyDown;
            coreWindow.KeyDown += CoreWindow_KeyDown;
        }
        private void CoreWindow_KeyDown(CoreWindow sender, KeyEventArgs args)
        {
            AnalyzeKeyEvent(args);
        }

        public void AnalyzeKeyEvent(KeyEventArgs arg)
        {
            SetCurrentDeviceId(GetDeviceId(arg));
        }

        private void SetCurrentDeviceId(string deviceId)
        {
            if (!_isEnable)
                return;

            var controllerType = GetGameControllerType(deviceId);
            if (CurrentControllerType == controllerType)
                return;

            CurrentControllerType = controllerType;
            RaisePropertyChanged(nameof(CurrentControllerType));
            GameControllerChanged?.Invoke(this, controllerType);
        }

        #region Property Changed
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        #endregion

    }
}
Comments are closed.