diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a05a4..ff4f8e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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**: diff --git a/Runtime/Haptics/HapticsService.cs b/Runtime/Haptics/HapticsService.cs index 943e695..5ae771f 100644 --- a/Runtime/Haptics/HapticsService.cs +++ b/Runtime/Haptics/HapticsService.cs @@ -90,7 +90,7 @@ public void PlayPresetDuration(HapticPreset preset, float duration = -1f) return; } - CancelPendingAutoStop(); + StopCurrentHaptic(); if (duration == 0f) { @@ -120,7 +120,7 @@ public void PlayCustom(float intensity01, float durationMs) return; } - CancelPendingAutoStop(); + StopCurrentHaptic(); intensity01 = Mathf.Clamp01(intensity01); _backend.PlayCustom(intensity01, durationMs); diff --git a/Tests/EditMode/Unit/HapticsServiceTest.cs b/Tests/EditMode/Unit/HapticsServiceTest.cs index 9ee4378..5137042 100644 --- a/Tests/EditMode/Unit/HapticsServiceTest.cs +++ b/Tests/EditMode/Unit/HapticsServiceTest.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using GameLovers.MobileServices.Haptics; using GameLovers.MobileServices.Haptics.Internal; using NUnit.Framework; @@ -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). @@ -197,6 +214,7 @@ private sealed class FakeHapticsBackend : IHapticsBackend public HapticPreset LastPreset; public float LastIntensity; public float LastDurationMs; + public List Operations = new List(); public bool IsSupported => IsSupportedValue; @@ -204,12 +222,14 @@ 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) @@ -217,11 +237,13 @@ 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() @@ -233,6 +255,7 @@ public void Reset() LastPreset = HapticPreset.None; LastIntensity = 0f; LastDurationMs = 0f; + Operations.Clear(); } } } diff --git a/Tests/PlayMode/Unit/HapticsServicePlayModeTest.cs b/Tests/PlayMode/Unit/HapticsServicePlayModeTest.cs index 1215554..6e6220c 100644 --- a/Tests/PlayMode/Unit/HapticsServicePlayModeTest.cs +++ b/Tests/PlayMode/Unit/HapticsServicePlayModeTest.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections.Generic; using GameLovers.MobileServices.Haptics; using GameLovers.MobileServices.Haptics.Internal; using NUnit.Framework; @@ -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. @@ -149,21 +163,24 @@ private sealed class FakeHapticsBackend : IHapticsBackend public int StopCount; public float LastIntensity; public float LastDurationMs; + public List Operations = new List(); 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"); } } } diff --git a/package.json b/package.json index c735b90..0f657c5 100644 --- a/package.json +++ b/package.json @@ -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).",