Skip to content

Commit

Permalink
Merge pull request ppy#6105 from hwsmm/sdl-cs-android
Browse files Browse the repository at this point in the history
Migrate Android framework to SDL
  • Loading branch information
bdach authored Apr 29, 2024
2 parents 169bd07 + a60499c commit 508d7e0
Show file tree
Hide file tree
Showing 23 changed files with 239 additions and 2,230 deletions.
31 changes: 0 additions & 31 deletions osu.Framework.Android/AndroidClipboard.cs

This file was deleted.

117 changes: 31 additions & 86 deletions osu.Framework.Android/AndroidGameActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Java.Lang;
using ManagedBass;
using osu.Framework.Bindables;
using Org.Libsdl.App;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Platform;
using Debug = System.Diagnostics.Debug;

namespace osu.Framework.Android
{
// since `ActivityAttribute` can't be inherited, the below is only provided as an illustrative example of how to setup an activity for best compatibility.
[Activity(ConfigurationChanges = DEFAULT_CONFIG_CHANGES, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
public abstract class AndroidGameActivity : Activity
public abstract class AndroidGameActivity : SDLActivity
{
protected const ConfigChanges DEFAULT_CONFIG_CHANGES = ConfigChanges.Keyboard
| ConfigChanges.KeyboardHidden
Expand All @@ -31,37 +32,35 @@ public abstract class AndroidGameActivity : Activity

protected const LaunchMode DEFAULT_LAUNCH_MODE = LaunchMode.SingleInstance;

internal static AndroidGameSurface Surface => (AndroidGameSurface)MSurface!;

private GameHost? host;

protected abstract Game CreateGame();

/// <summary>
/// Whether this <see cref="AndroidGameActivity"/> is active (in the foreground).
/// </summary>
public BindableBool IsActive { get; } = new BindableBool();
protected override string[] GetLibraries() => new string[] { "SDL3" };

protected override SDLSurface CreateSDLSurface(Context? context) => new AndroidGameSurface(this, context);

/// <summary>
/// The visibility flags for the system UI (status and navigation bars)
/// </summary>
public SystemUiFlags UIVisibilityFlags
protected override IRunnable CreateSDLMainRunnable() => new Runnable(() =>
{
#pragma warning disable 618 // SystemUiVisibility is deprecated
get => (SystemUiFlags)Window.AsNonNull().DecorView.SystemUiVisibility;
set
host = new AndroidGameHost(this);
host.AllowScreenSuspension.Result.BindValueChanged(allow =>
{
systemUiFlags = value;
Window.AsNonNull().DecorView.SystemUiVisibility = (StatusBarVisibility)value;
#pragma warning restore 618
}
}

private SystemUiFlags systemUiFlags;
RunOnUiThread(() =>
{
if (!allow.NewValue)
Window?.AddFlags(WindowManagerFlags.KeepScreenOn);
else
Window?.ClearFlags(WindowManagerFlags.KeepScreenOn);
});
}, true);
private AndroidGameView gameView = null!;
host.Run(CreateGame());
public override void OnTrimMemory([GeneratedEnum] TrimMemory level)
{
base.OnTrimMemory(level);
gameView.Host?.Collect();
}
if (!IsFinishing)
Finish();
});

protected override void OnCreate(Bundle? savedInstanceState)
{
Expand All @@ -75,82 +74,28 @@ protected override void OnCreate(Bundle? savedInstanceState)

base.OnCreate(savedInstanceState);

SetContentView(gameView = new AndroidGameView(this, CreateGame()));

UIVisibilityFlags = SystemUiFlags.LayoutFlags | SystemUiFlags.ImmersiveSticky | SystemUiFlags.HideNavigation | SystemUiFlags.Fullscreen;

// Firing up the on-screen keyboard (eg: interacting with textboxes) may cause the UI visibility flags to be altered thus showing the navigation bar and potentially the status bar
// This sets back the UI flags to hidden once the interaction with the on-screen keyboard has finished.
Window.AsNonNull().DecorView.SystemUiVisibilityChange += (_, e) =>
{
if ((SystemUiFlags)e.Visibility != systemUiFlags)
{
UIVisibilityFlags = systemUiFlags;
}
};

if (OperatingSystem.IsAndroidVersionAtLeast(28))
{
Window.AsNonNull().Attributes.AsNonNull().LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
}
}

gameView.HostStarted += host =>
{
host.AllowScreenSuspension.Result.BindValueChanged(allow =>
{
RunOnUiThread(() =>
{
if (!allow.NewValue)
Window?.AddFlags(WindowManagerFlags.KeepScreenOn);
else
Window?.ClearFlags(WindowManagerFlags.KeepScreenOn);
});
}, true);
};
public override void OnTrimMemory(TrimMemory level)
{
base.OnTrimMemory(level);
host?.Collect();
}

protected override void OnStop()
{
base.OnStop();
gameView.Host?.Suspend();
Bass.Pause();
}

protected override void OnRestart()
{
base.OnRestart();
gameView.Host?.Resume();
Bass.Start();
}

public override void OnWindowFocusChanged(bool hasFocus)
{
base.OnWindowFocusChanged(hasFocus);
IsActive.Value = hasFocus;
}

public override void OnBackPressed()
{
// Avoid the default implementation that does close the app.
// This only happens when the back button could not be captured from OnKeyDown.
}

// On some devices and keyboard combinations the OnKeyDown event does not propagate the key event to the view.
// Here it is done manually to ensure that the keys actually land in the view.

public override bool OnKeyDown([GeneratedEnum] Keycode keyCode, KeyEvent? e)
{
return gameView.OnKeyDown(keyCode, e);
}

public override bool OnKeyUp([GeneratedEnum] Keycode keyCode, KeyEvent? e)
{
return gameView.OnKeyUp(keyCode, e);
}

public override bool OnKeyLongPress([GeneratedEnum] Keycode keyCode, KeyEvent? e)
{
return gameView.OnKeyLongPress(keyCode, e);
}
}
}
37 changes: 13 additions & 24 deletions osu.Framework.Android/AndroidGameHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,27 @@
using Android.Content;
using osu.Framework.Android.Graphics.Textures;
using osu.Framework.Android.Graphics.Video;
using osu.Framework.Android.Input;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Midi;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Platform;
using Uri = Android.Net.Uri;

namespace osu.Framework.Android
{
public class AndroidGameHost : OsuTKGameHost
public class AndroidGameHost : SDL3GameHost
{
private readonly AndroidGameView gameView;
private readonly AndroidGameActivity activity;

public AndroidGameHost(AndroidGameView gameView)
public AndroidGameHost(AndroidGameActivity activity)
: base(string.Empty)
{
this.gameView = gameView;
this.activity = activity;
}

protected override void SetupConfig(IDictionary<FrameworkSetting, object> defaultOverrides)
Expand All @@ -42,28 +39,20 @@ protected override void SetupConfig(IDictionary<FrameworkSetting, object> defaul
base.SetupConfig(defaultOverrides);
}

protected override IWindow CreateWindow(GraphicsSurfaceType preferredSurface) => new AndroidGameWindow(gameView);
protected override IWindow CreateWindow(GraphicsSurfaceType preferredSurface) => new AndroidGameWindow(preferredSurface, Options.FriendlyGameName);

protected override Clipboard CreateClipboard() => new AndroidClipboard(gameView);
protected override void DrawFrame()
{
if (AndroidGameActivity.Surface.IsSurfaceReady)
base.DrawFrame();
}

public override bool CanExit => false;

public override bool CanSuspendToBackground => true;

public override bool OnScreenKeyboardOverlapsGameWindow => true;

protected override TextInputSource CreateTextInput() => new AndroidTextInput(gameView);

protected override IEnumerable<InputHandler> CreateAvailableInputHandlers() =>
new InputHandler[]
{
new AndroidMouseHandler(gameView),
new AndroidKeyboardHandler(gameView),
new AndroidTouchHandler(gameView),
new AndroidJoystickHandler(gameView),
new MidiHandler()
};

public override string InitialFileSelectorPath => @"/sdcard";

public override Storage GetStorage(string path) => new AndroidStorage(path, this);
Expand All @@ -87,7 +76,7 @@ public override void OpenUrlExternally(string url)
{
// Recommended way to open URLs on Android 11+
// https://developer.android.com/training/package-visibility/use-cases#open-urls-browser-or-other-app
gameView.Activity.StartActivity(intent);
activity.StartActivity(intent);
}
}
catch (Exception ex)
Expand All @@ -104,7 +93,7 @@ public override VideoDecoder CreateVideoDecoder(Stream stream)

public override bool SuspendToBackground()
{
return gameView.Activity.MoveTaskToBack(true);
return activity.MoveTaskToBack(true);
}
}
}
Loading

0 comments on commit 508d7e0

Please sign in to comment.