Skip to content

Commit

Permalink
Make beep on success. Added audio-path to audio file that will be pla…
Browse files Browse the repository at this point in the history
…yed on success Tick. Also added bool value that means play audio on success or not.

Also added (SettingsExtensions 2.0) with abstraction.
Extract MyTimer to ITimer interface, but it not make sense.

Make tests for Settings.
  • Loading branch information
Ar6yZuK authored and Ar6yZuK committed Mar 1, 2024
2 parents 8fba56b + b7073c3 commit 799c054
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 63 deletions.
3 changes: 2 additions & 1 deletion HabiticaHourUpVSIX/AppSettings/Models/UserSettingsModel.cs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
namespace HabiticaHourUpVSIX.AppSettings.Models;
public record struct UserSettingsModel(TimeSpan Divisor, string TaskIDToScoreUp, bool IsAutoScoreUp, bool ShowErrorOnFailure);
public record struct UserSettingsModel(TimeSpan Divisor, string TaskIDToScoreUp, bool IsAutoScoreUp, bool ShowErrorOnFailure,
bool BeepOnSuccess, string BeepAudioPath);
24 changes: 24 additions & 0 deletions HabiticaHourUpVSIX/AppSettings/UserSettings3.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions HabiticaHourUpVSIX/AppSettings/UserSettings3.settings
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,11 @@
<Setting Name="ShowErrorOnFailure" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="BeepOnSuccess" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="BeepAudioPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>
9 changes: 9 additions & 0 deletions HabiticaHourUpVSIX/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace HabiticaHourUpVSIX;

public static class DictionaryExtensions
{
public static TValue GetOrSet<TKey, TValue>(this Dictionary<TKey, TValue> obj1, TKey key, Func<TValue> resultProvider)
=> obj1.TryGetValue(key, out var result) ? result : obj1[key] = resultProvider();
}
15 changes: 14 additions & 1 deletion HabiticaHourUpVSIX/HabiticaHourUpVSIX.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@
<Compile Include="AppSettings\Models\HabiticaCredentials.cs" />
<Compile Include="AppSettings\Models\HabiticaSettingsModel.cs" />
<Compile Include="AppSettings\Models\SessionSettingsModel.cs" />
<Compile Include="DictionaryExtensions.cs" />
<Compile Include="Habitica\HabiticaClientAbstractions.cs" />
<Compile Include="IAudioPlayer.cs" />
<Compile Include="Validation\TimeSpanValidator.cs" />
<Compile Include="AppSettings\SettingsAbstractions.cs" />
<Compile Include="AppSettings\HabiticaSettings1.Designer.cs">
Expand Down Expand Up @@ -106,6 +108,7 @@
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>UserSettings3.Designer.cs</LastGenOutput>
</None>
<Compile Include="SettingsExtensions_v2.cs" />
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
<Generator>VsixManifestGenerator</Generator>
Expand Down Expand Up @@ -161,7 +164,17 @@
</PackageReference>
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.0.5232" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<COMReference Include="WMPLib">
<Guid>{6BF52A50-394A-11D3-B153-00C04F79FAA6}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<Page Include="ToolWindows\SettingsWindowControl.xaml">
<SubType>Designer</SubType>
Expand Down
23 changes: 22 additions & 1 deletion HabiticaHourUpVSIX/HabiticaHourUpVSIXPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,36 @@ namespace HabiticaHourUpVSIX;
[ProvideToolWindow(typeof(SettingsToolWindow.Pane))]
public sealed class HabiticaHourUpVSIXPackage : ToolkitPackage
{
private IAudioPlayer _soundPlayer;

public SettingsWithSaving<HabiticaSettingsModel> HabiticaSettingsReader { get; private set; }
public SettingsWithSaving<HabiticaCredentials> CredentialsSettings { get; private set; }
public SettingsWithSaving<UserSettingsModel> UserSettingsReader { get; private set; }
public Settings<SessionSettingsModel> SessionSettingsReader { get; private set; }
public HabiticaClientBase HabiticaClient { get; private set; }
public MyTimer Timer { get; private set; }
public ITimer Timer { get; private set; }

protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
await this.RegisterCommandsAsync();
this.RegisterToolWindows();

HabiticaClient = new HabiticaClient(this);
_soundPlayer = new AudioPlayer();

HabiticaClient.OnSuccessfullySend += delegate { SessionSettingsReader.AddSessionTicksSent(1); };
HabiticaClient.OnSuccessfullySend += delegate
{
var userSettings = UserSettingsReader.Read();
if (!userSettings.BeepOnSuccess)
return;

SetMusicPathIfNull(userSettings.BeepAudioPath);
_soundPlayer.Play();

void SetMusicPathIfNull(string beepMusicPath)
=> _soundPlayer.AudioPath = string.IsNullOrEmpty(_soundPlayer.AudioPath) ? beepMusicPath : _soundPlayer.AudioPath;
};

SessionSettingsReader = new SessionSettings();
CredentialsSettings = new CredentialsSettings();
Expand Down Expand Up @@ -122,4 +137,10 @@ private void AddTicksToAllSettings(int addedTicks)

HabiticaSettingsReader.Save();
}

protected override void Dispose(bool disposing)
{
Timer.Dispose();
base.Dispose(disposing);
}
}
26 changes: 26 additions & 0 deletions HabiticaHourUpVSIX/IAudioPlayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace HabiticaHourUpVSIX;
#nullable enable

internal interface IAudioPlayer
{
string? AudioPath { get; set; }
void Play();
}
internal class SoundPlayer : IAudioPlayer
{
private readonly System.Media.SoundPlayer _soundPlayer = new();

public string? AudioPath { get => _soundPlayer.SoundLocation; set => _soundPlayer.SoundLocation = value; }

public void Play()
=> _soundPlayer.PlaySync();
}
internal class AudioPlayer : IAudioPlayer
{
private readonly WMPLib.WindowsMediaPlayer _player = new();

public string? AudioPath { get => _player.URL; set => _player.URL = value; }

public void Play()
=> _player.controls.play();
}
57 changes: 49 additions & 8 deletions HabiticaHourUpVSIX/MyTimer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@
#nullable enable

namespace HabiticaHourUpVSIX;
public class MyTimer : IDisposable

public interface ITimer : IDisposable
{
event Action? Tick;

DateTime? Next { get; }
TimeSpan NextTick { get; }

bool Change(TimeSpan dueTime, TimeSpan period);
}
public class MyTimer : ITimer, IDisposable
{
private readonly Timer _timer;
private bool _disposedValue;

public event Action? Tick;

public TimeSpan Divisor { get; private set; }
public DateTime? Next { get; private set; }
public TimeSpan Period { get; protected set; }
public DateTime? Next { get; protected set; }

public TimeSpan NextTick => Next - DateTime.Now ?? Timeout.InfiniteTimeSpan;

Expand All @@ -18,17 +29,47 @@ public MyTimer()
_timer = new Timer(
delegate
{
Next = DateTime.Now.Add(Divisor);
Next = DateTime.Now.Add(Period);
Tick?.Invoke();
});
}

public bool Change(TimeSpan dueTime, TimeSpan divisor)
public bool Change(TimeSpan dueTime, TimeSpan period)
{
Divisor = divisor;
Period = period;
Next = DateTime.Now.Add(dueTime);
return _timer.Change(dueTime, divisor);
return _timer.Change(dueTime, period);
}

#region Dispose
protected virtual void Dispose(bool disposing)
{
if (_disposedValue)
return;

if (disposing)
{
_timer.Dispose();
// TODO: освободить управляемое состояние (управляемые объекты)
}

// TODO: освободить неуправляемые ресурсы (неуправляемые объекты) и переопределить метод завершения
// TODO: установить значение NULL для больших полей
_disposedValue = true;
}

public void Dispose() => _timer.Dispose();
// // TODO: переопределить метод завершения, только если "Dispose(bool disposing)" содержит код для освобождения неуправляемых ресурсов
// ~MyTimer()
// {
// // Не изменяйте этот код. Разместите код очистки в методе "Dispose(bool disposing)".
// Dispose(disposing: false);
// }

public void Dispose()
{
// Не изменяйте этот код. Разместите код очистки в методе "Dispose(bool disposing)".
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
2 changes: 1 addition & 1 deletion HabiticaHourUpVSIX/SettingsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace HabiticaHourUpVSIX;

// Need read before write, because there may be data that needs to be left untouched
public static class SettingsExtensions
public static partial class SettingsExtensions
{
public static void SetLastTickAfterWithSave(this SettingsWithSaving<HabiticaSettingsModel> obj1, TimeSpan lastTickToSet)
{
Expand Down
38 changes: 38 additions & 0 deletions HabiticaHourUpVSIX/SettingsExtensions_v2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using HabiticaHourUpVSIX;
using HabiticaHourUpVSIX.AppSettings.Abstractions;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

public static partial class SettingsExtensions
{
// Object as key because unknown T of SettingsWithSaving<T>
private static readonly Dictionary<(object, string propertyName), PropertyInfo> _cache = [];
private static readonly Dictionary<object, Type> _cachedTypes = [];

public static void SetWithSave<T, TProperty>(this SettingsWithSaving<T> obj1,
Expression<Func<T, TProperty>> expression,
TProperty value) where T : struct
{
if (expression.Body is not MemberExpression memberExpression)
throw new InvalidOperationException("Invalid expression. Should have property that returns.");
string memberName = memberExpression.Member.Name;

var settingsReadBoxed = (object)obj1.Read();
var property = GetProperty(obj1, memberName);

property.SetValue(settingsReadBoxed, value);

var settingsToWrite = (T)settingsReadBoxed;
obj1.Write(settingsToWrite);
obj1.Save();

PropertyInfo GetProperty(object obj1, string memberName)
{
Type type = _cachedTypes.GetOrSet(obj1, () => typeof(T));
PropertyInfo property = _cache.GetOrSet((obj1, memberName), () => type.GetProperty(memberName));

return property;
}
}
}
21 changes: 19 additions & 2 deletions HabiticaHourUpVSIX/ToolWindows/SettingsWindowControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
toolkit:Themes.UseVsTheme="True"
mc:Ignorable="d"
d:DesignHeight="370"
d:DesignHeight="450"
d:DesignWidth="327"
Name="MyToolWindow"
xmlns:local="clr-namespace:HabiticaHourUpVSIX"
Expand All @@ -34,6 +34,8 @@
<RowDefinition Height="36"/>
<RowDefinition Height="36"/>
<RowDefinition Height="36"/>
<RowDefinition Height="36"/>
<RowDefinition Height="36"/>
</Grid.RowDefinitions>

<Label Content="Total Ticks" DockPanel.Dock="Top" Grid.Row="0"/>
Expand Down Expand Up @@ -73,6 +75,21 @@

<Label Content="Show error on habitica score up failure" Grid.Row="9"/>
<CheckBox IsChecked="{Binding ShowErrorOnFailure}" Grid.Row="9" Grid.Column="1"/>
</Grid>

<Label Content="Beep on success tick" Grid.Row="10"/>
<CheckBox IsChecked="{Binding BeepOnSuccess}" Grid.Row="10" Grid.Column="1"/>

<Grid Grid.Row="11" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>

<Label Content="Beep audio file path "/>
<TextBox Text="{Binding OnBeepAudioPath}" Grid.Column="1"/>
<Button Content="&#x1F4C1;" Command="{Binding GetAudioBeepPathDialogCommand}" Margin="3.45" Grid.Column="2"/>
</Grid>
</Grid>
</ScrollViewer>
</UserControl>
Loading

0 comments on commit 799c054

Please sign in to comment.