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;