mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
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)
This commit is contained in:
parent
0a6baf670e
commit
6bccb3aab6
@ -7,6 +7,8 @@ using Android.OS;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
namespace osu.Android
|
namespace osu.Android
|
||||||
{
|
{
|
||||||
@ -19,6 +21,7 @@ namespace osu.Android
|
|||||||
: base(null)
|
: base(null)
|
||||||
{
|
{
|
||||||
gameActivity = activity;
|
gameActivity = activity;
|
||||||
|
powerStatus = new AndroidPowerStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Version AssemblyVersion
|
public override Version AssemblyVersion
|
||||||
@ -72,5 +75,11 @@ namespace osu.Android
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
||||||
|
public class AndroidPowerStatus : PowerStatus
|
||||||
|
{
|
||||||
|
public override double ChargeLevel => Battery.ChargeLevel;
|
||||||
|
|
||||||
|
public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,5 +63,9 @@
|
|||||||
<Version>5.0.0</Version>
|
<Version>5.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Used for obtaining battery info. -->
|
||||||
|
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@ -21,6 +21,8 @@ using osu.Game.Updater;
|
|||||||
using osu.Desktop.Windows;
|
using osu.Desktop.Windows;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
|
||||||
namespace osu.Desktop
|
namespace osu.Desktop
|
||||||
{
|
{
|
||||||
@ -33,6 +35,7 @@ namespace osu.Desktop
|
|||||||
: base(args)
|
: base(args)
|
||||||
{
|
{
|
||||||
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
|
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
|
||||||
|
powerStatus = new DefaultPowerStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override StableStorage GetStorageForStableInstall()
|
public override StableStorage GetStorageForStableInstall()
|
||||||
|
@ -25,6 +25,7 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -48,6 +49,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly VolumeOverlay volumeOverlay;
|
private readonly VolumeOverlay volumeOverlay;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private PowerStatus powerStatus { get; set; }
|
||||||
|
|
||||||
private readonly ChangelogOverlay changelogOverlay;
|
private readonly ChangelogOverlay changelogOverlay;
|
||||||
|
|
||||||
public TestScenePlayerLoader()
|
public TestScenePlayerLoader()
|
||||||
@ -93,26 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)));
|
LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the input manager child to a new test player loader container instance with a custom battery level
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="interactive">If the test player should behave like the production one.</param>
|
|
||||||
/// <param name="pluggedIn">If the player's device is plugged in.</param>
|
|
||||||
/// <param name="batteryLevel">A custom battery level for the test player.</param>
|
|
||||||
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<IApplicableToTrack>())
|
|
||||||
mod.ApplyToTrack(Beatmap.Value.Track);
|
|
||||||
|
|
||||||
loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive));
|
|
||||||
loader.batteryManager.ChargeLevel = batteryLevel;
|
|
||||||
loader.batteryManager.PluggedIn = pluggedIn;
|
|
||||||
LoadScreen(loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestEarlyExitBeforePlayerConstruction()
|
public void TestEarlyExitBeforePlayerConstruction()
|
||||||
{
|
{
|
||||||
@ -290,32 +274,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("wait for player load", () => player.IsLoaded);
|
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<bool>(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<FillFlowContainer<NotificationSection>>().First();
|
|
||||||
var notification = flowContainer.First();
|
|
||||||
|
|
||||||
InputManager.MoveMouseTo(notification);
|
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("wait for player load", () => player.IsLoaded);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestEpilepsyWarning(bool warning)
|
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<bool>(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<FillFlowContainer<NotificationSection>>().First();
|
||||||
|
var notification = flowContainer.First();
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(notification);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("wait for player load", () => player.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestEpilepsyWarningEarlyExit()
|
public void TestEpilepsyWarningEarlyExit()
|
||||||
{
|
{
|
||||||
@ -356,8 +338,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public IReadOnlyList<Mod> DisplayedMods => MetadataInfo.Mods.Value;
|
public IReadOnlyList<Mod> DisplayedMods => MetadataInfo.Mods.Value;
|
||||||
|
|
||||||
public new BatteryManager batteryManager => base.batteryManager;
|
|
||||||
|
|
||||||
public TestPlayerLoader(Func<Player> createPlayer)
|
public TestPlayerLoader(Func<Player> createPlayer)
|
||||||
: base(createPlayer)
|
: base(createPlayer)
|
||||||
{
|
{
|
||||||
|
@ -40,6 +40,7 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
||||||
|
|
||||||
@ -95,6 +96,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
protected Storage Storage { get; set; }
|
protected Storage Storage { get; set; }
|
||||||
|
|
||||||
|
protected PowerStatus powerStatus;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[Cached(typeof(IBindable<RulesetInfo>))]
|
[Cached(typeof(IBindable<RulesetInfo>))]
|
||||||
protected readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
protected readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||||
@ -329,6 +332,8 @@ namespace osu.Game
|
|||||||
dependencies.CacheAs(MusicController);
|
dependencies.CacheAs(MusicController);
|
||||||
|
|
||||||
Ruleset.BindValueChanged(onRulesetChanged);
|
Ruleset.BindValueChanged(onRulesetChanged);
|
||||||
|
|
||||||
|
dependencies.CacheAs(powerStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r)
|
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r)
|
||||||
|
@ -24,9 +24,9 @@ using osu.Game.Overlays.Notifications;
|
|||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using Xamarin.Essentials;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -113,6 +113,9 @@ namespace osu.Game.Screens.Play
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private AudioManager audioManager { get; set; }
|
private AudioManager audioManager { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private PowerStatus powerStatus { get; set; }
|
||||||
|
|
||||||
public PlayerLoader(Func<Player> createPlayer)
|
public PlayerLoader(Func<Player> createPlayer)
|
||||||
{
|
{
|
||||||
this.createPlayer = createPlayer;
|
this.createPlayer = createPlayer;
|
||||||
@ -265,7 +268,6 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -477,45 +479,18 @@ namespace osu.Game.Screens.Play
|
|||||||
#region Low battery warning
|
#region Low battery warning
|
||||||
private Bindable<bool> batteryWarningShownOnce;
|
private Bindable<bool> batteryWarningShownOnce;
|
||||||
|
|
||||||
// Send a warning if battery is less than 20%
|
|
||||||
public const double battery_tolerance = 0.2;
|
|
||||||
|
|
||||||
private void showBatteryWarningIfNeeded()
|
private void showBatteryWarningIfNeeded()
|
||||||
{
|
{
|
||||||
if (!batteryWarningShownOnce.Value)
|
if (!batteryWarningShownOnce.Value)
|
||||||
{
|
{
|
||||||
// Checks if the notification has not been shown yet, device is unplugged and if device battery is low.
|
// Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff
|
||||||
if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance)
|
if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel);
|
|
||||||
notificationOverlay?.Post(new BatteryWarningNotification());
|
notificationOverlay?.Post(new BatteryWarningNotification());
|
||||||
batteryWarningShownOnce.Value = true;
|
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
|
private class BatteryWarningNotification : SimpleNotification
|
||||||
{
|
{
|
||||||
|
@ -7,11 +7,16 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Tests
|
namespace osu.Game.Tests
|
||||||
{
|
{
|
||||||
public class OsuTestBrowser : OsuGameBase
|
public class OsuTestBrowser : OsuGameBase
|
||||||
{
|
{
|
||||||
|
public OsuTestBrowser()
|
||||||
|
{
|
||||||
|
powerStatus = new DefaultPowerStatus();
|
||||||
|
}
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
22
osu.Game/Utils/PowerStatus.cs
Normal file
22
osu.Game/Utils/PowerStatus.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum battery level before a warning notification
|
||||||
|
/// is sent.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -5,13 +5,30 @@ using System;
|
|||||||
using Foundation;
|
using Foundation;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
namespace osu.iOS
|
namespace osu.iOS
|
||||||
{
|
{
|
||||||
public class OsuGameIOS : OsuGame
|
public class OsuGameIOS : OsuGame
|
||||||
{
|
{
|
||||||
|
public OsuGameIOS()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
powerStatus = new IOSPowerStatus();
|
||||||
|
}
|
||||||
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,5 +116,9 @@
|
|||||||
<Visible>false</Visible>
|
<Visible>false</Visible>
|
||||||
</ImageAsset>
|
</ImageAsset>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Used for obtaining battery info. -->
|
||||||
|
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
Reference in New Issue
Block a user