Skip to content
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

Add feedback vibration using Taptic Engine on iOS #1338

Merged
merged 5 commits into from Jan 2, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,17 @@
# Unreleased

### Fuse.Vibration
- On iOS you can do feedback vibration by leveraging Taptic Engine. There are 9 types of vibration :
* Soft
* Rigid
* light
* Medium
* Heavy
* Success
* Warning
* Error
* Selection

# 1.13

## 1.13.0
Expand Down
49 changes: 44 additions & 5 deletions Source/Fuse.Vibration/Vibrate.uno
Expand Up @@ -3,8 +3,8 @@ using Fuse.Triggers.Actions;

namespace Fuse.Vibration
{
/** Vibrate the device for a duration
/** Vibrate the device for a duration or by vibration type (only on iOS)

You'll find this trigger action in the Fuse.Vibration package, which have to be referenced from your uno project.
For example:

Expand All @@ -17,22 +17,61 @@ namespace Fuse.Vibration
}

## Example

<StackPanel Margin="20">
<Button Margin="10" Text="Vibrate">
<Clicked>
<Vibrate Duration="5" />
</Clicked>
</Button>
</StackPanel>

On iOS you can do vibration by leveraging Taptic Engine. There are 9 types of vibration :
* Soft
* Rigid
* light
ichan-mb marked this conversation as resolved.
Show resolved Hide resolved
* Medium
* Heavy
* Success
* Warning
* Error
* Selection
To activate it, just pass those value to `VibrationType` property

##Example

<StackPanel Margin="20">
<!-- Works on iOS using Taptic Engine -->
<Button Margin="10" Text="Heavy Vibrate">
<Clicked>
<Vibrate VibrationType="Heavy" />
</Clicked>
</Button>
</StackPanel>
*/
public class Vibrate : TriggerAction
{
public double Duration { get; set;}


VibrationType _vibrationType = VibrationType.Undefined;
public VibrationType VibrationType
{
get
{
return _vibrationType;
}
set
{
_vibrationType = value;
}
}

protected override void Perform(Node target)
{
Vibration.Vibrate(Duration);
if (_vibrationType != VibrationType.Undefined)
Vibration.Feedback(_vibrationType);
else
Vibration.Vibrate(Duration);
}
}
}
188 changes: 188 additions & 0 deletions Source/Fuse.Vibration/Vibration.uno
Expand Up @@ -3,6 +3,20 @@ using Uno.Compiler.ExportTargetInterop;

namespace Fuse.Vibration
{
public enum VibrationType
{
Undefined,
Soft,
Rigid,
Light,
Medium,
Heavy,
Success,
Warning,
Error,
Selection
}

[ForeignInclude(Language.Java,
"android.os.Vibrator",
"android.app.Activity",
Expand All @@ -25,6 +39,168 @@ namespace Fuse.Vibration
@}
}

[Require("Source.Include", "UIKit/UIKit.h")]
[Require("Xcode.Framework", "AudioToolbox")]
[ForeignInclude(Language.ObjC, "AudioToolbox/AudioToolbox.h")]
class IOSTapticFeedback
{
public static extern(iOS) void Perform(VibrationType style)
{
switch (style)
{
case VibrationType.Soft:
PerformSoft();
break;
case VibrationType.Rigid:
PerformRigid();
break;
case VibrationType.Light:
PerformLight();
break;
case VibrationType.Medium:
PerformMedium();
break;
case VibrationType.Heavy:
PerformHeavy();
break;
case VibrationType.Success:
PerformSuccess();
break;
case VibrationType.Warning:
PerformWarning();
break;
case VibrationType.Error:
PerformError();
break;
case VibrationType.Selection:
PerformSelection();
break;
default:
PerformLight();
break;

}
}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformSoft()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UIImpactFeedbackGenerator * feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleSoft];
[feedback impactOccurred];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformRigid()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UIImpactFeedbackGenerator * feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleRigid];
[feedback impactOccurred];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformLight()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UIImpactFeedbackGenerator * feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
[feedback impactOccurred];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformMedium()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UIImpactFeedbackGenerator * feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
ichan-mb marked this conversation as resolved.
Show resolved Hide resolved
[feedback impactOccurred];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformHeavy()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UIImpactFeedbackGenerator * feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
ichan-mb marked this conversation as resolved.
Show resolved Hide resolved
[feedback impactOccurred];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformSuccess()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UINotificationFeedbackGenerator * feedback = [[UINotificationFeedbackGenerator alloc] init];
[feedback notificationOccurred:UINotificationFeedbackTypeSuccess];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformWarning()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UINotificationFeedbackGenerator * feedback = [[UINotificationFeedbackGenerator alloc] init];
[feedback notificationOccurred:UINotificationFeedbackTypeWarning];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformError()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UINotificationFeedbackGenerator * feedback = [[UINotificationFeedbackGenerator alloc] init];
[feedback notificationOccurred:UINotificationFeedbackTypeError];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}

[Foreign(Language.ObjC)]
static extern(iOS) void PerformSelection()
@{
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
dispatch_async(dispatch_get_main_queue(), ^{
UISelectionFeedbackGenerator * feedback = [[UISelectionFeedbackGenerator alloc] init];
[feedback selectionChanged];
[feedback prepare];
});
#else
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
#endif
@}
}

[Require("Xcode.Framework", "AudioToolbox")]
[ForeignInclude(Language.ObjC, "AudioToolbox/AudioToolbox.h")]
public static class Vibration
Expand All @@ -40,6 +216,18 @@ namespace Fuse.Vibration
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
@}

public static extern(Android) void Feedback(VibrationType type)
{
Permissions.Request(Permissions.Android.VIBRATE).Then(new AndroidVibrator(0.4).Done);
}

public static extern(iOS) void Feedback(VibrationType type)
{
IOSTapticFeedback.Perform(type);
}

public static extern(!MOBILE) void Vibrate(double seconds) { }

public static extern(!MOBILE) void Feedback(VibrationType type) { }
}
}
41 changes: 38 additions & 3 deletions Source/Fuse.Vibration/VibrationModule.uno
Expand Up @@ -16,6 +16,8 @@ namespace Fuse.Vibration

var vibration = require('FuseJS/Vibration');
vibration.vibrate(0.8);
// works on iOS using TapticEngine
vibration.vibrate('medium')
*/
[UXGlobalModule]
public sealed class VibrationModule : NativeModule
Expand All @@ -31,12 +33,45 @@ namespace Fuse.Vibration

/**
@scriptmethod vibrate(seconds)
@param seconds (double) seconds the vibration should last. 1 = 10 seconds, 0.5 = 5 seconds
@param seconds (double) seconds the vibration should last. 1 = 10 seconds, 0.5 = 5 seconds or
vibrationType (string) the type of vibration (works only on iOS using TapticEngine). Available vibrationType are : `soft`, `rigid`, `light`, `medium`, `heavy`, `success`, `warning`, `error`, `selection`
*/
static object[] Vibrate(Scripting.Context context, object[] args)
{
var seconds = (args.Length > 0) ? Marshal.ToDouble(args[0]) : 0.4;
Fuse.Vibration.Vibration.Vibrate(seconds);
if (args.Length > 0)
{
var vibrationType = args[0] as string;
if (vibrationType != null)
{
if (vibrationType == "soft")
Fuse.Vibration.Vibration.Feedback(VibrationType.Soft);
else if (vibrationType == "rigid")
Fuse.Vibration.Vibration.Feedback(VibrationType.Rigid);
else if (vibrationType == "light")
Fuse.Vibration.Vibration.Feedback(VibrationType.Light);
else if (vibrationType == "medium")
Fuse.Vibration.Vibration.Feedback(VibrationType.Medium);
else if (vibrationType == "heavy")
Fuse.Vibration.Vibration.Feedback(VibrationType.Heavy);
else if (vibrationType == "success")
Fuse.Vibration.Vibration.Feedback(VibrationType.Success);
else if (vibrationType == "warning")
Fuse.Vibration.Vibration.Feedback(VibrationType.Warning);
else if (vibrationType == "error")
Fuse.Vibration.Vibration.Feedback(VibrationType.Error);
else if (vibrationType == "selection")
Fuse.Vibration.Vibration.Feedback(VibrationType.Selection);
else
Fuse.Vibration.Vibration.Feedback(VibrationType.Soft);
}
else
{
var seconds = Marshal.ToDouble(args[0]);
Fuse.Vibration.Vibration.Vibrate(seconds);
}
}
else
Fuse.Vibration.Vibration.Vibrate(0.4);
return null;
}
}
Expand Down