From 0a6baf670eff5a90704b95277cf8a8b89dd44375 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Wed, 7 Apr 2021 14:41:21 -0400 Subject: [PATCH 01/14] Send a warning notification if device is unplugged and low battery - Uses Xamarin.Essentials in osu.Game.PlayerLoader to check battery level - Encapsulated battery checking in the public BatteryManager class so battery level and plugged in status can be accessed and edited in TestPlayerLoader - When checking battery level, catch NotImplementedException thrown by Xamarin.Essentials.Battery on non-mobile platforms - Added visual unit tests for battery notification To mock battery status and level, we had to define a batteryManager object in TestPlayerLoader and add a new function ResetPlayerWithBattery() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Marlina José --- osu.Android/Properties/AndroidManifest.xml | 1 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 48 +++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Configuration/SessionStatics.cs | 2 + osu.Game/Screens/Play/PlayerLoader.cs | 71 +++++++++++++++++++ osu.Game/osu.Game.csproj | 3 +- 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 770eaf2222..e717bab310 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -6,5 +6,6 @@ + \ No newline at end of file diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..a31d53e3b6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -93,6 +93,26 @@ namespace osu.Game.Tests.Visual.Gameplay LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } + /// + /// Sets the input manager child to a new test player loader container instance with a custom battery level + /// + /// If the test player should behave like the production one. + /// If the player's device is plugged in. + /// A custom battery level for the test player. + private void resetPlayerWithBattery(bool interactive, bool pluggedIn, double batteryLevel) + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; + + foreach (var mod in SelectedMods.Value.OfType()) + mod.ApplyToTrack(Beatmap.Value.Track); + + loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)); + loader.batteryManager.ChargeLevel = batteryLevel; + loader.batteryManager.PluggedIn = pluggedIn; + LoadScreen(loader); + } + [Test] public void TestEarlyExitBeforePlayerConstruction() { @@ -270,6 +290,32 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for player load", () => player.IsLoaded); } + [TestCase(false, 1.0)] // not plugged in, full battery, no notification + [TestCase(false, 0.2)] // not plugged in, at warning level, no notification + [TestCase(true, 0.1)] // plugged in, charging, below warning level, no notification + [TestCase(false, 0.1)] // not plugged in, below warning level, notification + public void TestLowBatteryNotification(bool pluggedIn, double batteryLevel) + { + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + + // mock phone on battery + AddStep("load player", () => resetPlayerWithBattery(false, pluggedIn, batteryLevel)); + AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); + int notificationCount = !pluggedIn && batteryLevel < PlayerLoader.battery_tolerance ? 1 : 0; + AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for player load", () => player.IsLoaded); + } + [TestCase(true)] [TestCase(false)] public void TestEpilepsyWarning(bool warning) @@ -310,6 +356,8 @@ namespace osu.Game.Tests.Visual.Gameplay public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; + public new BatteryManager batteryManager => base.batteryManager; + public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0e1f6f6b0c..df6d17f615 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 36eb6964dd..e8ee04731c 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,6 +16,7 @@ namespace osu.Game.Configuration { SetDefault(Static.LoginOverlayDisplayed, false); SetDefault(Static.MutedAudioNotificationShownOnce, false); + SetDefault(Static.BatteryLowNotificationShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -25,6 +26,7 @@ namespace osu.Game.Configuration { LoginOverlayDisplayed, MutedAudioNotificationShownOnce, + BatteryLowNotificationShownOnce, /// /// Info about seasonal backgrounds available fetched from API - see . diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 679b3c7313..01a51e6e7a 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -26,6 +26,7 @@ using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Users; using osuTK; using osuTK.Graphics; +using Xamarin.Essentials; namespace osu.Game.Screens.Play { @@ -121,6 +122,7 @@ namespace osu.Game.Screens.Play private void load(SessionStatics sessionStatics) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); + batteryWarningShownOnce = sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce); InternalChild = (content = new LogoTrackingContainer { @@ -196,6 +198,7 @@ namespace osu.Game.Screens.Play Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, Clock.CurrentTime + 1800, 0)); showMuteWarningIfNeeded(); + showBatteryWarningIfNeeded(); } public override void OnResuming(IScreen last) @@ -470,5 +473,73 @@ namespace osu.Game.Screens.Play } #endregion + + #region Low battery warning + private Bindable batteryWarningShownOnce; + + // Send a warning if battery is less than 20% + public const double battery_tolerance = 0.2; + + private void showBatteryWarningIfNeeded() + { + if (!batteryWarningShownOnce.Value) + { + // Checks if the notification has not been shown yet, device is unplugged and if device battery is low. + if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance) + { + Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel); + notificationOverlay?.Post(new BatteryWarningNotification()); + batteryWarningShownOnce.Value = true; + } + } + } + public BatteryManager batteryManager = new BatteryManager(); + public class BatteryManager + { + public double ChargeLevel; + public bool PluggedIn; + public BatteryManager() + { + // Attempt to get battery information using Xamarin.Essentials + // Xamarin.Essentials throws a NotSupportedOrImplementedException on .NET standard so this only works on iOS/Android + // https://github.com/xamarin/Essentials/blob/7218ab88f7fbe00ec3379bd54e6c0ce35ffc0c22/Xamarin.Essentials/Battery/Battery.netstandard.tvos.cs + try + { + ChargeLevel = Battery.ChargeLevel; + PluggedIn = Battery.PowerSource == BatteryPowerSource.Battery; + } + catch (NotImplementedException e) + { + Console.WriteLine("Could not access battery info: {0}", e); + ChargeLevel = 1.0; + PluggedIn = false; + } + } + } + + private class BatteryWarningNotification : SimpleNotification + { + public override bool IsImportant => true; + + public BatteryWarningNotification() + { + Text = "Your battery level is low! Charge your device to prevent interruptions."; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, NotificationOverlay notificationOverlay) + { + Icon = FontAwesome.Solid.BatteryQuarter; + IconBackgound.Colour = colours.RedDark; + + Activated = delegate + { + notificationOverlay.Hide(); + return true; + }; + } + } + + #endregion } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 292e5b932f..c68be5313d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 Library @@ -35,5 +35,6 @@ + From 6bccb3aab6487e0edd0d8030378c2f18c2868194 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 19:34:35 -0400 Subject: [PATCH 02/14] Use DI to implement battery detection, add BatteryCutoff property - Removed the Xamarin.Essentials package from osu.Game and added it to osu.iOS and osu.Android only. - iOS and Android implementations use Xamarin.Essentials.Battery, while the Desktop implementation only returns 100% battery for now. - Added a BatteryCutoff property to PowerStatus so it can be different for each platform (default 20%, 25% on iOS) --- osu.Android/OsuGameAndroid.cs | 9 +++ osu.Android/osu.Android.csproj | 4 + osu.Desktop/OsuGameDesktop.cs | 3 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 76 +++++++------------ osu.Game/OsuGameBase.cs | 5 ++ osu.Game/Screens/Play/PlayerLoader.cs | 37 ++------- osu.Game/Tests/OsuTestBrowser.cs | 5 ++ osu.Game/Utils/PowerStatus.cs | 22 ++++++ osu.iOS/OsuGameIOS.cs | 17 +++++ osu.iOS/osu.iOS.csproj | 4 + 10 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 osu.Game/Utils/PowerStatus.cs diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 21d6336b2c..4c82a175fc 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -7,6 +7,8 @@ using Android.OS; using osu.Framework.Allocation; using osu.Game; using osu.Game.Updater; +using osu.Game.Utils; +using Xamarin.Essentials; namespace osu.Android { @@ -19,6 +21,7 @@ namespace osu.Android : base(null) { gameActivity = activity; + powerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion @@ -72,5 +75,11 @@ namespace osu.Android } protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + public class AndroidPowerStatus : PowerStatus + { + public override double ChargeLevel => Battery.ChargeLevel; + + public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; + } } } diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 54857ac87d..64d5e5b1c8 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -63,5 +63,9 @@ 5.0.0 + + + + \ No newline at end of file diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 0c21c75290..b6c57b219d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -21,6 +21,8 @@ using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; +using osu.Game.Utils; +using osu.Framework.Allocation; namespace osu.Desktop { @@ -33,6 +35,7 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; + powerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index a31d53e3b6..c5cc10993c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -48,6 +49,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; + [Resolved] + private PowerStatus powerStatus { get; set; } + private readonly ChangelogOverlay changelogOverlay; public TestScenePlayerLoader() @@ -93,26 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } - /// - /// Sets the input manager child to a new test player loader container instance with a custom battery level - /// - /// If the test player should behave like the production one. - /// If the player's device is plugged in. - /// A custom battery level for the test player. - private void resetPlayerWithBattery(bool interactive, bool pluggedIn, double batteryLevel) - { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; - - foreach (var mod in SelectedMods.Value.OfType()) - mod.ApplyToTrack(Beatmap.Value.Track); - - loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)); - loader.batteryManager.ChargeLevel = batteryLevel; - loader.batteryManager.PluggedIn = pluggedIn; - LoadScreen(loader); - } - [Test] public void TestEarlyExitBeforePlayerConstruction() { @@ -290,32 +274,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for player load", () => player.IsLoaded); } - [TestCase(false, 1.0)] // not plugged in, full battery, no notification - [TestCase(false, 0.2)] // not plugged in, at warning level, no notification - [TestCase(true, 0.1)] // plugged in, charging, below warning level, no notification - [TestCase(false, 0.1)] // not plugged in, below warning level, notification - public void TestLowBatteryNotification(bool pluggedIn, double batteryLevel) - { - AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); - - // mock phone on battery - AddStep("load player", () => resetPlayerWithBattery(false, pluggedIn, batteryLevel)); - AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int notificationCount = !pluggedIn && batteryLevel < PlayerLoader.battery_tolerance ? 1 : 0; - AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); - AddStep("click notification", () => - { - var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); - var flowContainer = scrollContainer.Children.OfType>().First(); - var notification = flowContainer.First(); - - InputManager.MoveMouseTo(notification); - InputManager.Click(MouseButton.Left); - }); - - AddUntilStep("wait for player load", () => player.IsLoaded); - } - [TestCase(true)] [TestCase(false)] public void TestEpilepsyWarning(bool warning) @@ -334,6 +292,30 @@ namespace osu.Game.Tests.Visual.Gameplay } } + [TestCase(false, 1.0)] // not charging, full battery --> no warning + [TestCase(false, 0.2)] // not charging, at cutoff --> warning + [TestCase(false, 0.1)] // charging, below cutoff --> warning + public void TestLowBatteryNotification(bool isCharging, double chargeLevel) + { + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + + // set charge status and level + AddStep("load player", () => resetPlayer(false, () => { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); + AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); + int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; + AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for player load", () => player.IsLoaded); + } + [Test] public void TestEpilepsyWarningEarlyExit() { @@ -356,8 +338,6 @@ namespace osu.Game.Tests.Visual.Gameplay public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; - public new BatteryManager batteryManager => base.batteryManager; - public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 21313d0596..53873b0127 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -40,6 +40,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; +using osu.Game.Utils; using osuTK.Input; using RuntimeInfo = osu.Framework.RuntimeInfo; @@ -95,6 +96,8 @@ namespace osu.Game protected Storage Storage { get; set; } + protected PowerStatus powerStatus; + [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -329,6 +332,8 @@ namespace osu.Game dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); + + dependencies.CacheAs(powerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 01a51e6e7a..9710fa7e46 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -24,9 +24,9 @@ using osu.Game.Overlays.Notifications; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Users; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; -using Xamarin.Essentials; namespace osu.Game.Screens.Play { @@ -113,6 +113,9 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } + [Resolved] + private PowerStatus powerStatus { get; set; } + public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -265,7 +268,6 @@ namespace osu.Game.Screens.Play } #endregion - protected override void Update() { base.Update(); @@ -477,45 +479,18 @@ namespace osu.Game.Screens.Play #region Low battery warning private Bindable batteryWarningShownOnce; - // Send a warning if battery is less than 20% - public const double battery_tolerance = 0.2; - private void showBatteryWarningIfNeeded() { if (!batteryWarningShownOnce.Value) { - // Checks if the notification has not been shown yet, device is unplugged and if device battery is low. - if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance) + // Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff + if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff) { - Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel); notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; } } } - public BatteryManager batteryManager = new BatteryManager(); - public class BatteryManager - { - public double ChargeLevel; - public bool PluggedIn; - public BatteryManager() - { - // Attempt to get battery information using Xamarin.Essentials - // Xamarin.Essentials throws a NotSupportedOrImplementedException on .NET standard so this only works on iOS/Android - // https://github.com/xamarin/Essentials/blob/7218ab88f7fbe00ec3379bd54e6c0ce35ffc0c22/Xamarin.Essentials/Battery/Battery.netstandard.tvos.cs - try - { - ChargeLevel = Battery.ChargeLevel; - PluggedIn = Battery.PowerSource == BatteryPowerSource.Battery; - } - catch (NotImplementedException e) - { - Console.WriteLine("Could not access battery info: {0}", e); - ChargeLevel = 1.0; - PluggedIn = false; - } - } - } private class BatteryWarningNotification : SimpleNotification { diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index 71b0b02fa6..afce7dd38b 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -7,11 +7,16 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; +using osu.Game.Utils; namespace osu.Game.Tests { public class OsuTestBrowser : OsuGameBase { + public OsuTestBrowser() + { + powerStatus = new DefaultPowerStatus(); + } protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs new file mode 100644 index 0000000000..0657c98af1 --- /dev/null +++ b/osu.Game/Utils/PowerStatus.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Utils +{ + public abstract class PowerStatus + { + /// + /// The maximum battery level before a warning notification + /// is sent. + /// + public virtual double BatteryCutoff { get; } = 0.2; + public virtual double ChargeLevel { get; set; } + public virtual bool IsCharging { get; set; } + } + + public class DefaultPowerStatus : PowerStatus + { + public override double ChargeLevel { get; set; } = 1; + public override bool IsCharging { get; set; } = true; + } +} diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 5125ad81e0..21dce338dc 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -5,13 +5,30 @@ using System; using Foundation; using osu.Game; using osu.Game.Updater; +using osu.Game.Utils; +using Xamarin.Essentials; namespace osu.iOS { public class OsuGameIOS : OsuGame { + public OsuGameIOS() + : base(null) + { + powerStatus = new IOSPowerStatus(); + } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + + public class IOSPowerStatus : PowerStatus + { + // The low battery alert appears at 20% on iOS + // https://github.com/ppy/osu/issues/12239 + public override double BatteryCutoff => 0.25; + public override double ChargeLevel => Battery.ChargeLevel; + + public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; + } } } diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 1e9a21865d..ed6f52c60e 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -116,5 +116,9 @@ false + + + + From 493c095535c3acdddfada39ab29ecfd430c7afe9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 20:28:23 -0400 Subject: [PATCH 03/14] Fixed code style --- osu.Android/OsuGameAndroid.cs | 2 +- osu.Desktop/OsuGameDesktop.cs | 3 +-- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 6 +++++- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 1 + osu.Game/Tests/OsuTestBrowser.cs | 3 ++- osu.Game/Utils/PowerStatus.cs | 1 + osu.iOS/OsuGameIOS.cs | 2 +- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 4c82a175fc..eb0d499c8f 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -21,7 +21,7 @@ namespace osu.Android : base(null) { gameActivity = activity; - powerStatus = new AndroidPowerStatus(); + PowerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b6c57b219d..48e28fa251 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -22,7 +22,6 @@ using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; using osu.Game.Utils; -using osu.Framework.Allocation; namespace osu.Desktop { @@ -35,7 +34,7 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - powerStatus = new DefaultPowerStatus(); + PowerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index c5cc10993c..b885f0a9be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -300,7 +300,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); // set charge status and level - AddStep("load player", () => resetPlayer(false, () => { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); + AddStep("load player", () => resetPlayer(false, () => + { + powerStatus.IsCharging = isCharging; + powerStatus.ChargeLevel = chargeLevel; + })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 53873b0127..a907afd8af 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,7 +96,7 @@ namespace osu.Game protected Storage Storage { get; set; } - protected PowerStatus powerStatus; + protected PowerStatus PowerStatus; [Cached] [Cached(typeof(IBindable))] @@ -333,7 +333,7 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); - dependencies.CacheAs(powerStatus); + dependencies.CacheAs(PowerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 9710fa7e46..6ba99d65d5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -477,6 +477,7 @@ namespace osu.Game.Screens.Play #endregion #region Low battery warning + private Bindable batteryWarningShownOnce; private void showBatteryWarningIfNeeded() diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index afce7dd38b..d4fa2a11d3 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -15,8 +15,9 @@ namespace osu.Game.Tests { public OsuTestBrowser() { - powerStatus = new DefaultPowerStatus(); + PowerStatus = new DefaultPowerStatus(); } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 0657c98af1..1caed5b6fa 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -10,6 +10,7 @@ namespace osu.Game.Utils /// is sent. /// public virtual double BatteryCutoff { get; } = 0.2; + public virtual double ChargeLevel { get; set; } public virtual bool IsCharging { get; set; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 21dce338dc..d417fa8f8d 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -15,7 +15,7 @@ namespace osu.iOS public OsuGameIOS() : base(null) { - powerStatus = new IOSPowerStatus(); + PowerStatus = new IOSPowerStatus(); } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); From 6b6a71d3c38e47f2e6441cb6a03dcb04c43b51ac Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 20:38:16 -0400 Subject: [PATCH 04/14] trim whitespace --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index b885f0a9be..2a0f46f5ff 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -301,7 +301,7 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => - { + { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); From 59d13b0dd3faa9baca04c6664437294a2ed820f6 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 21:53:42 -0400 Subject: [PATCH 05/14] Fixed indentation sorry about the style fixes... I'm using JetBrains Rider from now on. --- osu.Android/OsuGameAndroid.cs | 1 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 18 +++++++++--------- osu.Game/Screens/Play/PlayerLoader.cs | 1 + 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index eb0d499c8f..a0c6a1e354 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -75,6 +75,7 @@ namespace osu.Android } protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + public class AndroidPowerStatus : PowerStatus { public override double ChargeLevel => Battery.ChargeLevel; diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 2a0f46f5ff..1377459c62 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -302,21 +302,21 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.IsCharging = isCharging; - powerStatus.ChargeLevel = chargeLevel; + powerStatus.IsCharging = isCharging; + powerStatus.ChargeLevel = chargeLevel; })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); AddStep("click notification", () => - { - var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); - var flowContainer = scrollContainer.Children.OfType>().First(); - var notification = flowContainer.First(); + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); - InputManager.MoveMouseTo(notification); - InputManager.Click(MouseButton.Left); - }); + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); AddUntilStep("wait for player load", () => player.IsLoaded); } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6ba99d65d5..e1ab0b8ef5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -268,6 +268,7 @@ namespace osu.Game.Screens.Play } #endregion + protected override void Update() { base.Update(); From 08311abc5ec93907337976e0d8ae0209684ef769 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 9 Apr 2021 17:55:41 -0400 Subject: [PATCH 06/14] Remove setters, cache CreatePowerStatus() and use a dummy LocalPowerStatus class in test scene --- osu.Android/OsuGameAndroid.cs | 7 ++- osu.Desktop/OsuGameDesktop.cs | 2 - .../Visual/Gameplay/TestScenePlayerLoader.cs | 44 +++++++++++++++---- osu.Game/Configuration/SessionStatics.cs | 4 +- osu.Game/OsuGame.cs | 5 +++ osu.Game/OsuGameBase.cs | 4 +- osu.Game/Screens/Play/PlayerLoader.cs | 11 ++--- osu.Game/Tests/OsuTestBrowser.cs | 6 --- osu.Game/Utils/PowerStatus.cs | 26 ++++++----- osu.iOS/OsuGameIOS.cs | 12 ++--- 10 files changed, 74 insertions(+), 47 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index a0c6a1e354..02f4fa1ad6 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -21,7 +21,6 @@ namespace osu.Android : base(null) { gameActivity = activity; - PowerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion @@ -76,8 +75,12 @@ namespace osu.Android protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - public class AndroidPowerStatus : PowerStatus + protected override PowerStatus CreatePowerStatus() => new AndroidPowerStatus(); + + private class AndroidPowerStatus : PowerStatus { + public override double BatteryCutoff => 0.20; + public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 48e28fa251..0c21c75290 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -21,7 +21,6 @@ using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; -using osu.Game.Utils; namespace osu.Desktop { @@ -34,7 +33,6 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - PowerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 1377459c62..36958b0741 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; - [Resolved] - private PowerStatus powerStatus { get; set; } + [Cached(typeof(PowerStatus))] + private readonly LocalPowerStatus powerStatus = new LocalPowerStatus(); private readonly ChangelogOverlay changelogOverlay; @@ -292,22 +292,22 @@ namespace osu.Game.Tests.Visual.Gameplay } } - [TestCase(false, 1.0)] // not charging, full battery --> no warning + [TestCase(false, 1.0)] // not charging, above cutoff --> no warning [TestCase(false, 0.2)] // not charging, at cutoff --> warning - [TestCase(false, 0.1)] // charging, below cutoff --> warning + [TestCase(true, 0.1)] // charging, below cutoff --> no warning public void TestLowBatteryNotification(bool isCharging, double chargeLevel) { - AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.IsCharging = isCharging; - powerStatus.ChargeLevel = chargeLevel; + powerStatus.SetCharging(isCharging); + powerStatus.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; - AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + int warning = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; + AddAssert($"notification {(warning == 1 ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == warning); AddStep("click notification", () => { var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); @@ -380,5 +380,31 @@ namespace osu.Game.Tests.Visual.Gameplay throw new TimeoutException(); } } + + /// + /// Mutable dummy PowerStatus class for + /// + /// + private class LocalPowerStatus : PowerStatus + { + private bool isCharging = true; + private double chargeLevel = 1; + + public override double BatteryCutoff => 0.2; + + public override bool IsCharging => isCharging; + + public override double ChargeLevel => chargeLevel; + + public void SetCharging(bool value) + { + isCharging = value; + } + + public void SetChargeLevel(double value) + { + chargeLevel = value; + } + } } } diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index e8ee04731c..71e1a1efcc 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { SetDefault(Static.LoginOverlayDisplayed, false); SetDefault(Static.MutedAudioNotificationShownOnce, false); - SetDefault(Static.BatteryLowNotificationShownOnce, false); + SetDefault(Static.LowBatteryNotificationShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -26,7 +26,7 @@ namespace osu.Game.Configuration { LoginOverlayDisplayed, MutedAudioNotificationShownOnce, - BatteryLowNotificationShownOnce, + LowBatteryNotificationShownOnce, /// /// Info about seasonal backgrounds available fetched from API - see . diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 809e5d3c1b..277455b7d3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -694,6 +694,11 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); + if (CreatePowerStatus() != null) + { + dependencies.CacheAs(CreatePowerStatus()); + } + chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index a907afd8af..1b10de614a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,7 +96,7 @@ namespace osu.Game protected Storage Storage { get; set; } - protected PowerStatus PowerStatus; + protected virtual PowerStatus CreatePowerStatus() => null; [Cached] [Cached(typeof(IBindable))] @@ -332,8 +332,6 @@ namespace osu.Game dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); - - dependencies.CacheAs(PowerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e1ab0b8ef5..d342bf8273 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private PowerStatus powerStatus { get; set; } public PlayerLoader(Func createPlayer) @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play private void load(SessionStatics sessionStatics) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); - batteryWarningShownOnce = sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce); + batteryWarningShownOnce = sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce); InternalChild = (content = new LogoTrackingContainer { @@ -483,10 +483,11 @@ namespace osu.Game.Screens.Play private void showBatteryWarningIfNeeded() { + if (powerStatus == null) return; + if (!batteryWarningShownOnce.Value) { - // Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff - if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff) + if (powerStatus.IsLowBattery) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; @@ -500,7 +501,7 @@ namespace osu.Game.Screens.Play public BatteryWarningNotification() { - Text = "Your battery level is low! Charge your device to prevent interruptions."; + Text = "Your battery level is low! Charge your device to prevent interruptions during gameplay."; } [BackgroundDependencyLoader] diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index d4fa2a11d3..71b0b02fa6 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -7,17 +7,11 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; -using osu.Game.Utils; namespace osu.Game.Tests { public class OsuTestBrowser : OsuGameBase { - public OsuTestBrowser() - { - PowerStatus = new DefaultPowerStatus(); - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 1caed5b6fa..77d531710e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -3,21 +3,27 @@ namespace osu.Game.Utils { + /// + /// Provides access to the system's power status. + /// Currently implemented on iOS and Android only. + /// public abstract class PowerStatus { /// - /// The maximum battery level before a warning notification - /// is sent. + /// The maximum battery level considered as low, from 0 to 1. /// - public virtual double BatteryCutoff { get; } = 0.2; + public virtual double BatteryCutoff { get; } = 0; - public virtual double ChargeLevel { get; set; } - public virtual bool IsCharging { get; set; } - } + /// + /// The charge level of the battery, from 0 to 1. + /// + public virtual double ChargeLevel { get; } = 0; - public class DefaultPowerStatus : PowerStatus - { - public override double ChargeLevel { get; set; } = 1; - public override bool IsCharging { get; set; } = true; + public virtual bool IsCharging { get; } = false; + + /// + /// Returns true if = false and <= . + /// + public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index d417fa8f8d..b53b594eae 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -12,20 +12,16 @@ namespace osu.iOS { public class OsuGameIOS : OsuGame { - public OsuGameIOS() - : base(null) - { - PowerStatus = new IOSPowerStatus(); - } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - public class IOSPowerStatus : PowerStatus + protected override PowerStatus CreatePowerStatus() => new IOSPowerStatus(); + + private class IOSPowerStatus : PowerStatus { - // The low battery alert appears at 20% on iOS - // https://github.com/ppy/osu/issues/12239 public override double BatteryCutoff => 0.25; + public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; From 2b947a44da2d320669f61d13d6c1ee8c7c660001 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:00:37 +0300 Subject: [PATCH 07/14] Cache power status at base instead --- osu.Game/OsuGame.cs | 5 ----- osu.Game/OsuGameBase.cs | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 277455b7d3..809e5d3c1b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -694,11 +694,6 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); - if (CreatePowerStatus() != null) - { - dependencies.CacheAs(CreatePowerStatus()); - } - chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 41d790ea4a..fec8272076 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -284,6 +284,11 @@ namespace osu.Game dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); + + var powerStatus = CreatePowerStatus(); + if (powerStatus != null) + dependencies.CacheAs(powerStatus); + dependencies.Cache(new SessionStatics()); dependencies.Cache(new OsuColour()); From 3d85dc11c62aedccda43b373326463c9d5be5162 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:02:32 +0300 Subject: [PATCH 08/14] Adjust documentation --- osu.Game/Utils/PowerStatus.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 77d531710e..a53f50b620 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -22,7 +22,8 @@ namespace osu.Game.Utils public virtual bool IsCharging { get; } = false; /// - /// Returns true if = false and <= . + /// Whether the battery is currently low in charge. + /// Returns true if not charging and current charge level is lower than or equal to . /// public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; } From 07ee1b4d0b73edebaaffc632acd13dc993f0871c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:11:28 +0300 Subject: [PATCH 09/14] Make power status properties abstract --- osu.Game/Utils/PowerStatus.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index a53f50b620..46f7e32b9e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -12,14 +12,14 @@ namespace osu.Game.Utils /// /// The maximum battery level considered as low, from 0 to 1. /// - public virtual double BatteryCutoff { get; } = 0; + public abstract double BatteryCutoff { get; } /// /// The charge level of the battery, from 0 to 1. /// - public virtual double ChargeLevel { get; } = 0; + public abstract double ChargeLevel { get; } - public virtual bool IsCharging { get; } = false; + public abstract bool IsCharging { get; } /// /// Whether the battery is currently low in charge. From cb947a3b2710d1c22a55a1ac91262c83bc68591c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 10:02:51 +0300 Subject: [PATCH 10/14] Add expected output in test case rather than determining internally --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 36958b0741..657c1dd47e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -292,10 +292,10 @@ namespace osu.Game.Tests.Visual.Gameplay } } - [TestCase(false, 1.0)] // not charging, above cutoff --> no warning - [TestCase(false, 0.2)] // not charging, at cutoff --> warning - [TestCase(true, 0.1)] // charging, below cutoff --> no warning - public void TestLowBatteryNotification(bool isCharging, double chargeLevel) + [TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning + [TestCase(true, 0.1, false)] // charging, below cutoff --> no warning + [TestCase(false, 0.2, true)] // not charging, at cutoff --> warning + public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn) { AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); @@ -306,8 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay powerStatus.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int warning = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; - AddAssert($"notification {(warning == 1 ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == warning); + AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0)); AddStep("click notification", () => { var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); From 419fd4470cbb818298cd5fe324d8ff528b2f3901 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 08:57:44 +0300 Subject: [PATCH 11/14] Reorder method declaration --- osu.Game/OsuGameBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fec8272076..96aabf0024 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,8 +96,6 @@ namespace osu.Game protected Storage Storage { get; set; } - protected virtual PowerStatus CreatePowerStatus() => null; - [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -159,6 +157,8 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); + protected virtual PowerStatus CreatePowerStatus() => null; + /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. /// From b7e16c2fcc8cad36ab2f40cc59c3f254088624e9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 11 Apr 2021 15:47:38 -0400 Subject: [PATCH 12/14] Remove Xamarin.Essentials package from main project --- osu.Game/osu.Game.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d0a918a8f5..471eced55a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,6 +35,5 @@ - From 43b97fe0ad738854f1ef795bfd386db615c75458 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 10:52:12 -0400 Subject: [PATCH 13/14] Refactor PowerStatus (now called BatteryInfo) --- osu.Android/OsuGameAndroid.cs | 6 ++---- osu.Android/osu.Android.csproj | 1 - .../Visual/Gameplay/TestScenePlayerLoader.cs | 14 ++++++-------- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- osu.Game/Utils/{PowerStatus.cs => BatteryInfo.cs} | 12 +++--------- osu.iOS/OsuGameIOS.cs | 6 ++---- osu.iOS/osu.iOS.csproj | 1 - 8 files changed, 18 insertions(+), 32 deletions(-) rename osu.Game/Utils/{PowerStatus.cs => BatteryInfo.cs} (68%) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 02f4fa1ad6..050bf2b787 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -75,12 +75,10 @@ namespace osu.Android protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - protected override PowerStatus CreatePowerStatus() => new AndroidPowerStatus(); + protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo(); - private class AndroidPowerStatus : PowerStatus + private class AndroidBatteryInfo : BatteryInfo { - public override double BatteryCutoff => 0.20; - public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 64d5e5b1c8..582c856a47 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -64,7 +64,6 @@ - diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 657c1dd47e..311448a284 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; - [Cached(typeof(PowerStatus))] - private readonly LocalPowerStatus powerStatus = new LocalPowerStatus(); + [Cached(typeof(BatteryInfo))] + private readonly LocalBatteryInfo batteryInfo = new LocalBatteryInfo(); private readonly ChangelogOverlay changelogOverlay; @@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.SetCharging(isCharging); - powerStatus.SetChargeLevel(chargeLevel); + batteryInfo.SetCharging(isCharging); + batteryInfo.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0)); @@ -381,16 +381,14 @@ namespace osu.Game.Tests.Visual.Gameplay } /// - /// Mutable dummy PowerStatus class for + /// Mutable dummy BatteryInfo class for /// /// - private class LocalPowerStatus : PowerStatus + private class LocalBatteryInfo : BatteryInfo { private bool isCharging = true; private double chargeLevel = 1; - public override double BatteryCutoff => 0.2; - public override bool IsCharging => isCharging; public override double ChargeLevel => chargeLevel; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 96aabf0024..de8ba93106 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -157,7 +157,7 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); - protected virtual PowerStatus CreatePowerStatus() => null; + protected virtual BatteryInfo CreateBatteryInfo() => null; /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. @@ -285,7 +285,7 @@ namespace osu.Game dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); - var powerStatus = CreatePowerStatus(); + var powerStatus = CreateBatteryInfo(); if (powerStatus != null) dependencies.CacheAs(powerStatus); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d342bf8273..964410f838 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Play private AudioManager audioManager { get; set; } [Resolved(CanBeNull = true)] - private PowerStatus powerStatus { get; set; } + private BatteryInfo batteryInfo { get; set; } public PlayerLoader(Func createPlayer) { @@ -483,11 +483,11 @@ namespace osu.Game.Screens.Play private void showBatteryWarningIfNeeded() { - if (powerStatus == null) return; + if (batteryInfo == null) return; if (!batteryWarningShownOnce.Value) { - if (powerStatus.IsLowBattery) + if (batteryInfo.IsLowBattery) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/BatteryInfo.cs similarity index 68% rename from osu.Game/Utils/PowerStatus.cs rename to osu.Game/Utils/BatteryInfo.cs index 46f7e32b9e..1a64213d8e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/BatteryInfo.cs @@ -5,15 +5,9 @@ namespace osu.Game.Utils { /// /// Provides access to the system's power status. - /// Currently implemented on iOS and Android only. /// - public abstract class PowerStatus + public abstract class BatteryInfo { - /// - /// The maximum battery level considered as low, from 0 to 1. - /// - public abstract double BatteryCutoff { get; } - /// /// The charge level of the battery, from 0 to 1. /// @@ -23,8 +17,8 @@ namespace osu.Game.Utils /// /// Whether the battery is currently low in charge. - /// Returns true if not charging and current charge level is lower than or equal to . + /// Returns true if not charging and current charge level is lower than or equal to 25%. /// - public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; + public bool IsLowBattery => !IsCharging && ChargeLevel <= 0.25; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index b53b594eae..702aef45f5 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -16,12 +16,10 @@ namespace osu.iOS protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - protected override PowerStatus CreatePowerStatus() => new IOSPowerStatus(); + protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo(); - private class IOSPowerStatus : PowerStatus + private class IOSBatteryInfo : BatteryInfo { - public override double BatteryCutoff => 0.25; - public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index ed6f52c60e..1cbe4422cc 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -117,7 +117,6 @@ - From f66306a81ad70868c29bc2a56d471a08f6bebbe5 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 11:11:22 -0400 Subject: [PATCH 14/14] Remove IsLowBattery --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Utils/BatteryInfo.cs | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 311448a284..c56f2db0d0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -294,7 +294,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning [TestCase(true, 0.1, false)] // charging, below cutoff --> no warning - [TestCase(false, 0.2, true)] // not charging, at cutoff --> warning + [TestCase(false, 0.25, true)] // not charging, at cutoff --> warning public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn) { AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 964410f838..fc4659da97 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -487,7 +487,7 @@ namespace osu.Game.Screens.Play if (!batteryWarningShownOnce.Value) { - if (batteryInfo.IsLowBattery) + if (!batteryInfo.IsCharging && batteryInfo.ChargeLevel <= 0.25) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; diff --git a/osu.Game/Utils/BatteryInfo.cs b/osu.Game/Utils/BatteryInfo.cs index 1a64213d8e..dd9b695e1f 100644 --- a/osu.Game/Utils/BatteryInfo.cs +++ b/osu.Game/Utils/BatteryInfo.cs @@ -14,11 +14,5 @@ namespace osu.Game.Utils public abstract double ChargeLevel { get; } public abstract bool IsCharging { get; } - - /// - /// Whether the battery is currently low in charge. - /// Returns true if not charging and current charge level is lower than or equal to 25%. - /// - public bool IsLowBattery => !IsCharging && ChargeLevel <= 0.25; } }