-
Notifications
You must be signed in to change notification settings - Fork 750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updated sample for the issue Add sample for SimpleHapticsController #473 #13994
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,83 @@ | ||
#nullable enable | ||
#pragma warning disable CS0618 // obsolete members | ||
#nullable enable | ||
#program warning disable CS0618 // obsolete members | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using Android.App; | ||
using Android.Views; | ||
using Android.Provider; | ||
|
||
using Uno.Extensions; | ||
using Uno.UI; | ||
using PhoneVibrationDevice = Windows.Phone.Devices.Notification.VibrationDevice; | ||
using Uno.Foundation.Logging; | ||
|
||
namespace Windows.Devices.Haptics | ||
{ | ||
public partial class SimpleHapticsController | ||
{ | ||
public IReadOnlyList<SimpleHapticsControllerFeedback> SupportedFeedback { get; } = new SimpleHapticsControllerFeedback[] | ||
{ | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(100)), | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Press, TimeSpan.FromMilliseconds(300)) | ||
}; | ||
public partial class SimpleHapticsController | ||
{ | ||
public IReadOnlyList<SimpleHapticsControllerFeedback> SupportedFeedback { get; } = new SimpleHapticsControllerFeedback[] | ||
{ | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Press, TimeSpan.FromMilliseconds(300)), | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(100)), // Single Click | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(150)) // Double Click | ||
}; | ||
|
||
public void SendHapticFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback is null) | ||
{ | ||
throw new ArgumentNullException(nameof(feedback)); | ||
} | ||
public void SendHapticFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback is null) | ||
{ | ||
throw new ArgumentNullException(nameof(feedback)); | ||
} | ||
|
||
if (ContextHelper.Current == null) | ||
{ | ||
throw new InvalidOperationException($"Context must be initialized before {nameof(SendHapticFeedback)} is called."); | ||
} | ||
try | ||
{ | ||
var activity = (Activity)ContextHelper.Current; | ||
var androidFeedback = FeedbackToAndroidFeedback(feedback); | ||
#pragma warning disable CA1422 // Validate platform compatibility | ||
bool hapticFeedbackEnabled = Settings.System.GetInt(activity.ContentResolver, Settings.System.HapticFeedbackEnabled, 0) != 0; | ||
#pragma warning restore CA1422 // Validate platform compatibility | ||
if (hapticFeedbackEnabled) | ||
{ | ||
var executed = activity.Window?.DecorView.PerformHapticFeedback(androidFeedback) ?? false; | ||
if (!executed && PhoneVibrationDevice.GetDefault() is { } vibrationDevice) | ||
{ | ||
// Fall back to VibrationDevice | ||
vibrationDevice.Vibrate(feedback.Duration); | ||
} | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
if (this.Log().IsEnabled(LogLevel.Error)) | ||
{ | ||
this.Log().LogError($"Could not send haptic feedback: {ex}"); | ||
} | ||
} | ||
} | ||
Vibrator vibrator = (Vibrator)Android.App.Application.Context.GetSystemService(Android.Content.Context.VibratorService); | ||
|
||
private static FeedbackConstants FeedbackToAndroidFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Click) | ||
{ | ||
return FeedbackConstants.ContextClick; | ||
} | ||
else if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Press) | ||
{ | ||
return FeedbackConstants.LongPress; | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback waveform"); | ||
} | ||
} | ||
} | ||
if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Press) | ||
{ | ||
if (vibrator.HasVibrator) | ||
{ | ||
vibrator.Vibrate((long)feedback.Duration.TotalMilliseconds); | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Device does not support vibration"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably best if we skip vibrating altogether. It may be common for simulators to not have this feature. We can probably log a debug message instead. |
||
} | ||
} | ||
else if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Click) | ||
{ | ||
if (feedback.Duration.TotalMilliseconds <= 100) | ||
{ | ||
// Single Click | ||
if (vibrator.HasVibrator) | ||
{ | ||
vibrator.Vibrate(VibrationEffect.CreateOneShot((long)feedback.Duration.TotalMilliseconds, VibrationEffect.DefaultAmplitude)); | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Device does not support vibration"); | ||
} | ||
} | ||
else if (feedback.Duration.TotalMilliseconds <= 200) | ||
{ | ||
// Double Click | ||
if (vibrator.HasVibrator) | ||
{ | ||
long[] pattern = { 0, (long)feedback.Duration.TotalMilliseconds, 50, (long)feedback.Duration.TotalMilliseconds }; | ||
vibrator.Vibrate(VibrationEffect.CreateWaveform(pattern, -1)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be keeping this try/catch, in case there's an unexpected error. Failing haptics should not fail the app. |
||
else | ||
{ | ||
throw new NotSupportedException("Device does not support vibration"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, let's log instead. |
||
} | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback duration for Click waveform"); | ||
} | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback waveform"); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,66 @@ | ||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using UIKit; | ||
public class HapticFeedbackManager | ||
{ | ||
private DateTime? lastClickTime = null; | ||
|
||
#nullable enable | ||
public IReadOnlyList<SimpleHapticsControllerFeedback> SupportedFeedback { get; } = new SimpleHapticsControllerFeedback[] | ||
{ | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(100)), | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Press, TimeSpan.FromMilliseconds(300)), | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(50)), // Single Click | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(150)), // Double Click | ||
}; | ||
|
||
namespace Windows.Devices.Haptics | ||
{ | ||
public partial class SimpleHapticsController | ||
{ | ||
public IReadOnlyList<SimpleHapticsControllerFeedback> SupportedFeedback { get; } = new SimpleHapticsControllerFeedback[] | ||
{ | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(100)), | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Press, TimeSpan.FromMilliseconds(300)) | ||
}; | ||
public void SendHapticFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback is null) | ||
{ | ||
throw new ArgumentNullException(nameof(feedback)); | ||
} | ||
|
||
public void SendHapticFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback is null) | ||
{ | ||
throw new ArgumentNullException(nameof(feedback)); | ||
} | ||
var impactStyle = FeedbackToImpactStyle(feedback); | ||
using var impact = new UIImpactFeedbackGenerator(impactStyle); | ||
impact.Prepare(); | ||
|
||
var impactStyle = FeedbackToImpactStyle(feedback); | ||
using var impact = new UIImpactFeedbackGenerator(impactStyle); | ||
impact.Prepare(); | ||
impact.ImpactOccurred(); | ||
} | ||
if (feedback.Duration.TotalMilliseconds <= 100) | ||
{ | ||
// Single Click | ||
impact.ImpactOccurred(); | ||
} | ||
else if (feedback.Duration.TotalMilliseconds <= 200) | ||
{ | ||
// Potential Double Click - Check time interval with last click | ||
if (lastClickTime.HasValue && (DateTime.Now - lastClickTime.Value).TotalMilliseconds < 200) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dr1rrb how do we determine that something is a double tap in gestures? We may want to align this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are using this https://github.com/unoplatform/uno/blob/master/src/Uno.UWP/UI/Input/GestureRecognizer.cs#L27 But anyway, I'm not sure to understand the condition and why try to make distinction between simple click and double click. Here we end by doing |
||
{ | ||
// Double Click | ||
impact.ImpactOccurred(); | ||
} | ||
else | ||
{ | ||
// Treat as a single click if not a double click | ||
lastClickTime = DateTime.Now; | ||
impact.ImpactOccurred(); | ||
} | ||
} | ||
else | ||
{ | ||
// Handle other feedback types as before | ||
impact.ImpactOccurred(); | ||
} | ||
} | ||
|
||
private UIImpactFeedbackStyle FeedbackToImpactStyle(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Click) | ||
{ | ||
return UIImpactFeedbackStyle.Light; | ||
} | ||
else if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Press) | ||
{ | ||
return UIImpactFeedbackStyle.Medium; | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback waveform"); | ||
} | ||
} | ||
} | ||
private UIImpactFeedbackStyle FeedbackToImpactStyle(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Click) | ||
{ | ||
return UIImpactFeedbackStyle.Light; | ||
} | ||
else if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Press) | ||
{ | ||
return UIImpactFeedbackStyle.Medium; | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback waveform"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,54 @@ | ||
#nullable enable | ||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using AppKit; | ||
|
||
namespace Windows.Devices.Haptics | ||
{ | ||
public partial class SimpleHapticsController | ||
{ | ||
public IReadOnlyList<SimpleHapticsControllerFeedback> SupportedFeedback { get; } = new SimpleHapticsControllerFeedback[] | ||
{ | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Press, TimeSpan.FromMilliseconds(300)) | ||
}; | ||
public partial class SimpleHapticsController | ||
{ | ||
public IReadOnlyList<SimpleHapticsControllerFeedback> SupportedFeedback { get; } = new SimpleHapticsControllerFeedback[] | ||
{ | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(100)), // Single Click | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Click, TimeSpan.FromMilliseconds(150)), // Double Click | ||
new SimpleHapticsControllerFeedback(KnownSimpleHapticsControllerWaveforms.Press, TimeSpan.FromMilliseconds(300)) | ||
}; | ||
|
||
public void SendHapticFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback is null) | ||
{ | ||
throw new ArgumentNullException(nameof(feedback)); | ||
} | ||
public void SendHapticFeedback(SimpleHapticsControllerFeedback feedback) | ||
{ | ||
if (feedback is null) | ||
{ | ||
throw new ArgumentNullException(nameof(feedback)); | ||
} | ||
|
||
if (feedback.Waveform != KnownSimpleHapticsControllerWaveforms.Press) | ||
{ | ||
throw new NotSupportedException("Unsupported feedback waveform"); | ||
} | ||
|
||
NSHapticFeedbackManager.DefaultPerformer.PerformFeedback( | ||
NSHapticFeedbackPattern.Generic, | ||
NSHapticFeedbackPerformanceTime.Default); | ||
} | ||
} | ||
if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Click) | ||
{ | ||
// Determine if it's a single click or double click based on the duration | ||
if (feedback.Duration.TotalMilliseconds <= 100) | ||
{ | ||
// Single Click | ||
NSHapticFeedbackManager.DefaultPerformer.PerformFeedback(NSHapticFeedbackPattern.Generic, NSHapticFeedbackPerformanceTime.Default); | ||
} | ||
else if (feedback.Duration.TotalMilliseconds <= 200) | ||
{ | ||
// Double Click | ||
NSHapticFeedbackManager.DefaultPerformer.PerformFeedback(NSHapticFeedbackPattern.Generic, NSHapticFeedbackPerformanceTime.Default); | ||
NSHapticFeedbackManager.DefaultPerformer.PerformFeedback(NSHapticFeedbackPattern.Generic, NSHapticFeedbackPerformanceTime.Default); | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback duration for Click waveform"); | ||
} | ||
} | ||
else if (feedback.Waveform == KnownSimpleHapticsControllerWaveforms.Press) | ||
{ | ||
NSHapticFeedbackManager.DefaultPerformer.PerformFeedback(NSHapticFeedbackPattern.Generic, NSHapticFeedbackPerformanceTime.Default); | ||
} | ||
else | ||
{ | ||
throw new NotSupportedException("Unsupported feedback waveform"); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.