Skip to content
Merged
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.1] - 2026-08-12

**Fixed**:
- Starting a new preset or custom haptic now stops any active output before playback begins.

## [1.0.0] - 2026-08-10

**New**:
Expand Down
4 changes: 2 additions & 2 deletions Runtime/Haptics/HapticsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public void PlayPresetDuration(HapticPreset preset, float duration = -1f)
return;
}

CancelPendingAutoStop();
StopCurrentHaptic();

if (duration == 0f)
{
Expand Down Expand Up @@ -120,7 +120,7 @@ public void PlayCustom(float intensity01, float durationMs)
return;
}

CancelPendingAutoStop();
StopCurrentHaptic();

intensity01 = Mathf.Clamp01(intensity01);
_backend.PlayCustom(intensity01, durationMs);
Expand Down
23 changes: 23 additions & 0 deletions Tests/EditMode/Unit/HapticsServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using GameLovers.MobileServices.Haptics;
using GameLovers.MobileServices.Haptics.Internal;
using NUnit.Framework;
Expand Down Expand Up @@ -145,6 +146,22 @@ public void PlayPresetDuration_NegativeOrDefault_CallsLoopNoAutoStop()
Assert.IsTrue(_haptics.IsPlaying);
}

[Test]
// ADMIT: HapticsService.PlayPresetDuration could start a new output without stopping an active loop.
// RCR: HapticsService.cs PlayPresetDuration — `StopCurrentHaptic()` → `CancelPendingAutoStop()` → RED (StopCount expected 1 was 0).
public void PlayPresetDuration_WhilePlaying_StopsPreviousBackendBeforeNewPreset()
{
_haptics.PlayPresetDuration(HapticPreset.Warning);
_backend.Reset();

_haptics.PlayPreset(HapticPreset.Success);

Assert.AreEqual(1, _backend.StopCount);
Assert.AreEqual(1, _backend.OneShotCount);
Assert.AreEqual("Stop,OneShot", string.Join(",", _backend.Operations));
Assert.AreEqual(HapticPreset.Success, _backend.LastPreset);
}

[Test]
// ADMIT: HapticsService.PlayCustom could accept durationMs == 0 and schedule a zero-length haptic plus a host coroutine.
// RCR: HapticsService.cs PlayCustom — `durationMs <= 0f` → `durationMs < 0f` → RED (CustomCount expected 0 was 1).
Expand Down Expand Up @@ -197,31 +214,36 @@ private sealed class FakeHapticsBackend : IHapticsBackend
public HapticPreset LastPreset;
public float LastIntensity;
public float LastDurationMs;
public List<string> Operations = new List<string>();

public bool IsSupported => IsSupportedValue;

public void PlayPresetOneShot(HapticPreset preset)
{
OneShotCount++;
LastPreset = preset;
Operations.Add("OneShot");
}

public void PlayPresetLoop(HapticPreset preset)
{
LoopCount++;
LastPreset = preset;
Operations.Add("Loop");
}

public void PlayCustom(float intensity01, float durationMs)
{
CustomCount++;
LastIntensity = intensity01;
LastDurationMs = durationMs;
Operations.Add("Custom");
}

public void Stop()
{
StopCount++;
Operations.Add("Stop");
}

public void Reset()
Expand All @@ -233,6 +255,7 @@ public void Reset()
LastPreset = HapticPreset.None;
LastIntensity = 0f;
LastDurationMs = 0f;
Operations.Clear();
}
}
}
Expand Down
21 changes: 19 additions & 2 deletions Tests/PlayMode/Unit/HapticsServicePlayModeTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using GameLovers.MobileServices.Haptics;
using GameLovers.MobileServices.Haptics.Internal;
using NUnit.Framework;
Expand Down Expand Up @@ -98,6 +99,19 @@ public void PlayCustom_ClampsIntensity01()
Assert.AreEqual(0.42f, _backend.LastIntensity, 1e-6f);
}

[Test]
// ADMIT: HapticsService.PlayCustom could start custom output without stopping an active loop.
// RCR: HapticsService.cs PlayCustom — `StopCurrentHaptic()` → `CancelPendingAutoStop()` → RED (StopCount expected 1 was 0).
public void PlayCustom_WhilePlaying_StopsPreviousBackendBeforeCustom()
{
_haptics.PlayPresetDuration(HapticPreset.Warning);

_haptics.PlayCustom(0.5f, 250f);

Assert.AreEqual(1, _backend.StopCount);
Assert.AreEqual("Loop,Stop,Custom", string.Join(",", _backend.Operations));
}

[UnityTest]
// ADMIT: destroying HapticsHost mid-countdown must not fire the auto-stop callback into a service
// whose host is gone.
Expand Down Expand Up @@ -149,21 +163,24 @@ private sealed class FakeHapticsBackend : IHapticsBackend
public int StopCount;
public float LastIntensity;
public float LastDurationMs;
public List<string> Operations = new List<string>();

public bool IsSupported => IsSupportedValue;

public void PlayPresetOneShot(HapticPreset preset) { }
public void PlayPresetLoop(HapticPreset preset) { }
public void PlayPresetOneShot(HapticPreset preset) { Operations.Add("OneShot"); }
public void PlayPresetLoop(HapticPreset preset) { Operations.Add("Loop"); }

public void PlayCustom(float intensity01, float durationMs)
{
LastIntensity = intensity01;
LastDurationMs = durationMs;
Operations.Add("Custom");
}

public void Stop()
{
StopCount++;
Operations.Add("Stop");
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "com.gamelovers.mobileservices",
"displayName": "Mobile Services",
"author": "Miguel Tomas",
"version": "1.0.0",
"version": "1.0.1",
"unity": "6000.0",
"license": "MIT",
"description": "Mobile platform services for Unity: native UI (alerts/toasts), push notifications, and gesture detection (swipe/drag).",
Expand Down
Loading