Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ namespace BrickController2.DeviceManagement.Lego;
/// <summary>
/// Represents a LEGO® Powered Up 88010 Remote Control
/// </summary>
internal class RemoteControl : BluetoothDevice
internal class RemoteControl : BluetoothDevice, IDynamicInputDevice
{
private const string ENABLED_SETTING_NAME = "RemoteControlEnabled";
private const bool DEFAULT_ENABLED = false;

private IGattCharacteristic? _characteristic;
private InputDeviceBase<RemoteControl>? _inputController;
private IInputDeviceConnector? _inputDeviceConnector;

public RemoteControl(string name, string address, IEnumerable<NamedSetting> settings, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
: base(name, address, deviceRepository, bleService)
Expand All @@ -41,14 +41,14 @@ public RemoteControl(string name, string address, IEnumerable<NamedSetting> sett

public override void SetOutput(int channel, float value) => throw new InvalidOperationException();

internal void ConnectInputController<TController>(TController inputController) where TController : InputDeviceBase<RemoteControl>
public void ConnectInputController(IInputDeviceConnector inputController)
{
_inputController = inputController;
_inputDeviceConnector = inputController;
}

internal void DisconnectInputController()
public void DisconnectInputController()
{
_inputController = default;
_inputDeviceConnector = default;
}

internal void ResetEvents() => RaiseButtonEvents(
Expand Down Expand Up @@ -156,16 +156,16 @@ private void OnButtonEvents(string plus, string stop, string minus, ReadOnlySpan

private void RaiseButtonEvents((string eventName, float value)[] buttonEvents)
{
if (_inputController is null)
if (_inputDeviceConnector is null)
{
return;
}

var events = buttonEvents
.Where(e => _inputController.HasValueChanged(e.eventName, e.value))
.Where(e => _inputDeviceConnector.HasValueChanged(e.eventName, e.value))
.ToDictionary(e => (InputDeviceEventType.Button, e.eventName), e => e.value);

_inputController.RaiseEvent(events);
_inputDeviceConnector.RaiseEvent(events);
}

private static float GetButtonValue(byte flag) => flag != 0 ? BUTTON_PRESSED : BUTTON_RELEASED;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace BrickController2.PlatformServices.InputDevice;

internal interface IDynamicInputDevice
{
void ConnectInputController(IInputDeviceConnector controller);
void DisconnectInputController();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.Generic;

namespace BrickController2.PlatformServices.InputDevice;

internal interface IInputDeviceConnector
{
internal bool HasValueChanged(string axisName, float value);

internal void RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BrickController2.PlatformServices.InputDevice;
/// abstract base class for input devices
/// </summary>
/// <typeparam name="TInputDeviceDevice">Type of native instance of inputdevice device</typeparam>
public abstract class InputDeviceBase<TInputDeviceDevice> : IInputDevice
public abstract class InputDeviceBase<TInputDeviceDevice> : IInputDevice, IInputDeviceConnector
where TInputDeviceDevice : class
{
/// <summary>stored last value per axis to detect changes</summary>
Expand Down Expand Up @@ -66,7 +66,7 @@ public virtual void Stop()

protected bool ContainsAxisValue(string axisName) => _lastAxisValues.ContainsKey(axisName);

protected internal bool HasValueChanged(string axisName, float value)
public bool HasValueChanged(string axisName, float value)
{
// get last reported value or the default one
_lastAxisValues.TryGetValue(axisName, out float lastValue);
Expand All @@ -80,7 +80,7 @@ protected internal bool HasValueChanged(string axisName, float value)
return true;
}

protected internal void RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
public void RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
{
if (!events.Any())
{
Expand Down
21 changes: 21 additions & 0 deletions BrickController2/BrickController2/UI/Pages/DevicePage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,27 @@
</VerticalStackLayout>
</VerticalStackLayout>

<!-- Input events -->
<VerticalStackLayout IsVisible="{Binding IsInputDevice}">
<CollectionView ItemsSource="{Binding InputEventList}" SelectionMode="None" ItemsLayout="VerticalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<VerticalStackLayout Padding="{OnIdiom '10,5,10,5', Desktop='10,0,10,0'}">
<HorizontalStackLayout>
<Label Text="{Binding EventCode}" FontSize="Large" FontAttributes="Bold"/>
<Label Text=":" FontSize="Large" Margin="5,0,5,0"/>
<Label Text="{Binding Value}" FontSize="Large"/>
</HorizontalStackLayout>
<Label Text="{Binding EventType}" FontSize="Medium"/>
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.EmptyView>
<Label Text="{extensions:Translate PressButtonsOrMoveJoys}" Style="{StaticResource CollectionViewEmptyLabelStyle}"/>
</CollectionView.EmptyView>
</CollectionView>
</VerticalStackLayout>

</VerticalStackLayout>
</ScrollView>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -8,19 +9,22 @@
using BrickController2.CreationManagement;
using BrickController2.DeviceManagement;
using BrickController2.Helpers;
using BrickController2.PlatformServices.InputDevice;
using BrickController2.UI.Commands;
using BrickController2.UI.Services.Navigation;
using BrickController2.UI.Services.Dialog;
using BrickController2.UI.Services.Translation;
using Device = BrickController2.DeviceManagement.Device;
using static BrickController2.CreationManagement.ControllerDefaults;
using static BrickController2.PlatformServices.InputDevice.InputDevices;

namespace BrickController2.UI.ViewModels
{
public class DevicePageViewModel : PageViewModelBase
public class DevicePageViewModel : PageViewModelBase, IInputDeviceConnector
{
private readonly IDeviceManager _deviceManager;
private readonly IDialogService _dialogService;
private readonly Dictionary<string, float> _lastAxisValues = [];

private CancellationTokenSource? _connectionTokenSource;
private Task? _connectionTask;
Expand All @@ -32,6 +36,7 @@
ITranslationService translationService,
IDeviceManager deviceManager,
IDialogService dialogService,
IServiceProvider serviceProvider,
NavigationParameters parameters)
: base(navigationService, translationService)
{
Expand All @@ -57,6 +62,7 @@
}

public Device Device { get; }
internal IDynamicInputDevice? InputDevice => Device as IDynamicInputDevice;
public bool IsBuWizzDevice => Device.DeviceType == DeviceType.BuWizz;
public bool IsBuWizz2Device => Device.DeviceType == DeviceType.BuWizz2;
public bool CanBePowerSource => Device.CanBePowerSource;
Expand All @@ -70,6 +76,8 @@

public bool IsAdvertisingDevice => Device is BluetoothAdvertisingDevice;

public bool IsInputDevice => Device is IDynamicInputDevice;

public bool IsServoOrStepperSupported => DeviceOutputs.Any(x => x.IsServoOrStepperSupported);

public ICommand RenameCommand { get; }
Expand All @@ -84,6 +92,8 @@

public IEnumerable<DeviceOutputViewModel> DeviceOutputs { get; }

public ObservableCollection<InputDeviceEventViewModel> InputEventList { get; } = new();

public override async void OnAppearing()
{
_isDisappearing = false;
Expand All @@ -104,6 +114,9 @@
}
}

// connect input device if available
InputDevice?.ConnectInputController(this);

_connectionTokenSource = new CancellationTokenSource();
_connectionTask = ConnectAsync();
}
Expand All @@ -112,6 +125,9 @@
{
base.OnDisappearing();

// disconnect input device if available
InputDevice?.DisconnectInputController();

if (_connectionTokenSource is not null && _connectionTask is not null)
{
_connectionTokenSource?.Cancel();
Expand All @@ -121,6 +137,42 @@
await Device.DisconnectAsync();
}


bool IInputDeviceConnector.HasValueChanged(string axisName, float value)
{
// get last reported value or the default one
_lastAxisValues.TryGetValue(axisName, out float lastValue);
// skip value if there is no change
if (AreAlmostEqual(value, lastValue))
{
return false;
}
// persist
_lastAxisValues[axisName] = value;
return true;
}

void IInputDeviceConnector.RaiseEvent(IDictionary<(InputDeviceEventType, string), float> events)
{
foreach (var inputDeviceEvent in events)
{
var item = InputEventList.FirstOrDefault(x => x.EventCode == inputDeviceEvent.Key.Item2);

if (AXIS_DELTA_VALUE >= Math.Abs(inputDeviceEvent.Value))
{
InputEventList.Remove(item);

Check warning on line 163 in BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs

View workflow job for this annotation

GitHub Actions / BrickController Core Build

Possible null reference argument for parameter 'item' in 'bool Collection<InputDeviceEventViewModel>.Remove(InputDeviceEventViewModel item)'.

Check warning on line 163 in BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs

View workflow job for this annotation

GitHub Actions / BrickController Core Build

Possible null reference argument for parameter 'item' in 'bool Collection<InputDeviceEventViewModel>.Remove(InputDeviceEventViewModel item)'.

Check warning on line 163 in BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs

View workflow job for this annotation

GitHub Actions / BrickController WinUI Build

Possible null reference argument for parameter 'item' in 'bool Collection<InputDeviceEventViewModel>.Remove(InputDeviceEventViewModel item)'.

Check warning on line 163 in BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs

View workflow job for this annotation

GitHub Actions / BrickController WinUI Build

Possible null reference argument for parameter 'item' in 'bool Collection<InputDeviceEventViewModel>.Remove(InputDeviceEventViewModel item)'.

Check warning on line 163 in BrickController2/BrickController2/UI/ViewModels/DevicePageViewModel.cs

View workflow job for this annotation

GitHub Actions / BrickController Android Build

Possible null reference argument for parameter 'item' in 'bool Collection<InputDeviceEventViewModel>.Remove(InputDeviceEventViewModel item)'.
}
else if (item != null)
{
item.Value = inputDeviceEvent.Value;
}
else
{
InputEventList.Add(new InputDeviceEventViewModel(inputDeviceEvent.Key.Item1, inputDeviceEvent.Key.Item2, inputDeviceEvent.Value));
}
}
}

private async Task RenameDeviceAsync()
{
try
Expand Down