From 926c06da9cdc542465d6f7a7785fd8fd1cfbc483 Mon Sep 17 00:00:00 2001 From: CoderGamester Date: Wed, 12 Aug 2026 20:31:53 +0300 Subject: [PATCH 1/5] docs: modernize mobile service guidance --- AGENTS.md | 2 +- CHANGELOG.md | 7 +- README.md | 256 +++++------------------ Samples~/MobileServicesSamples/README.md | 2 + docs/README.md | 6 +- package.json | 2 +- 6 files changed, 66 insertions(+), 209 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 969fca8..1ef1496 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ ## 1. Package Overview - **Package**: `com.gamelovers.mobileservices` -- **Unity**: Unity 6 only. Supported streams are 6000.5.x, 6000.3.x, and 6000.0.x. Validation editors are 6000.5.7f1 (primary), 6000.3.21f1, and 6000.0.81f1; other streams are unsupported/untested. +- **Unity**: minimum 6000.0; compatibility reference streams 6000.0.x, 6000.3.x, and 6000.5.x. Reference editors: 6000.0.81f1, 6000.3.21f1, 6000.5.7f1 (primary). Do not call a stream validated without current matrix artifacts. - **Dependencies** (see `package.json`) - `com.unity.mobile.notifications` (**2.3.0**) - `com.unity.inputsystem` (**1.11.0**) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4f8e3..b504d97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,12 @@ 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). +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +**Changed**: +- Declared Unity 6000.0 as the package minimum and documented 6000.0.x, 6000.3.x, and 6000.5.x as compatibility reference streams. ## [1.0.1] - 2026-08-12 diff --git a/README.md b/README.md index f786b40..6020606 100644 --- a/README.md +++ b/README.md @@ -1,238 +1,88 @@ # GameLovers Mobile Services -[![Unity Version](https://img.shields.io/badge/Unity-6000.0%20%7C%206000.3%20%7C%206000.5-blue.svg)](https://unity3d.com/get-unity/download) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Version](https://img.shields.io/github/v/tag/CoderGamester/com.gamelovers.mobileservices?label=version)](CHANGELOG.md) +Unity 6 services for local notifications, native UI, haptics, permissions, App Tracking Transparency, deep links, gestures, and mobile build tooling. -> **Quick Links**: [Installation](#installation) | [Quick Start](#quick-start) | [Services](#services-at-a-glance) | [Samples](#samples) | [Related docs](#related-docs) | [Contributing](#contributing) +[![Unity](https://img.shields.io/badge/Unity-6000.0%20%7C%206000.3%20%7C%206000.5-blue.svg)](https://unity.com/download) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md) +[![Version](https://img.shields.io/github/v/tag/CoderGamester/Unity-MobileServices?label=version)](CHANGELOG.md) -## Why Use This Package? +## Scope -Building mobile-specific features in Unity often requires dealing with platform-specific code, native bridges, and fragmented APIs. This **Mobile Services** package consolidates essential mobile functionality into a unified, easy-to-use API: +Use Mobile Services to isolate platform-specific behavior behind Unity-friendly APIs. It provides **local** notifications, not remote push delivery; it also does not provide connectivity or store fallback services. The package is pipeline-neutral. -| Problem | Solution | -|---------|----------| -| **Platform-specific UI code** | Native UI service bridges iOS/Android alerts, toasts, review prompts, and share sheets with one API | -| **Notification complexity** | Notification service wraps Unity Mobile Notifications with channel management + a fluent `service.Schedule().In(...).Title(...).Send()` builder | -| **Custom gesture detection** | Gesture controller provides swipe and tap detection via Unity's EnhancedTouch | -| **Haptic plugin sprawl** | Zero-dependency `IHapticsService` with 9 presets, custom intensity, and time-bounded looping — built directly on iOS/Android primitives | -| **Scattered device APIs** | One `IDeviceService` umbrella over `SafeArea`, `ScreenWake`, `Battery`, `AudioSession`, `Permissions`, `Att`, `DeepLink` — each child also independently mockable | -| **Deep-link routing boilerplate** | `IDeepLinkRouter.MapRoute("/promo/:id", handler)` over `IDeepLinkService` | -| **iOS silent switch muting audio** | `device.AudioSession.ConfigureForPlayback()` overrides `AVAudioSession` category in one line | -| **iOS App Tracking Transparency** | `device.Att.RequestAuthorizationAsync()` — direct `ATTrackingManager` bridge, no `com.unity.ads.ios-support` dependency | -| **Cold-start deep link loss** | `device.DeepLink` queues the launch link for the first subscriber so you never miss it | -| **Forgotten `Info.plist` keys → App Store rejection** | Mobile Services Config asset + build postprocessor auto-inject `NS*UsageDescription` keys (localized per device language), entitlements, and Android manifest entries; fail-fast validation lists every missing key | -| **Editor testing challenges** | A Device Simulator plugin panel paints platform-shaped mocks inside the simulated phone (edit + play) with live diagnostics; `EditorPlatformSimulator` drives state for unit tests | +## Unity compatibility -**Built for production:** Uses Unity's official Mobile Notifications and Input System packages. Tested in real mobile games. +| Item | Current policy | +| --- | --- | +| Minimum Unity version | `6000.0` | +| Reference streams | `6000.0.x`, `6000.3.x`, `6000.5.x` | +| Reference editors | `6000.0.81f1`, `6000.3.21f1`, `6000.5.7f1` (primary) | +| Render pipeline | Pipeline-neutral | +| Validation status | Compatibility target; do not treat a stream as validated until the repository matrix records it. | ---- +| Platform | Intended behavior | +| --- | --- | +| iOS / Android | Native services and build-time configuration | +| Editor | Platform simulator and no-op/mock backends where applicable | +| Standalone | Limited fallback behavior; haptics reports unsupported | +| WebGL | Not supported | -## System Requirements - -- **[Unity](https://unity.com/download)** Unity 6 only. The supported validation matrix is: - - | Stream | Exact validation editor | - |---|---| - | Primary 6000.5.x | 6000.5.7f1 | - | Supported 6000.3.x | 6000.3.21f1 | - | Supported 6000.0.x | 6000.0.81f1 | - - Other Unity versions are unsupported/untested. -- **[Unity Mobile Notifications](https://docs.unity3d.com/Packages/com.unity.mobile.notifications@latest)** (2.3.0) — automatically resolved -- **[Unity Input System](https://docs.unity3d.com/Packages/com.unity.inputsystem@latest)** (1.11.0) — automatically resolved; set **Active Input Handling** to **Input System Package (New)** or **Both** so Unity 6 routes the sample's UI Toolkit input through InputForUI, without a uGUI dependency - -| Platform | Status | -|---|---| -| iOS | ✅ Fully supported | -| Android | ✅ Fully supported | -| Editor | ✅ Supported (no-op fallbacks + truth-mirror simulator) | -| Standalone | ⚠️ Gestures + SafeArea + Battery (level/status); Haptics returns `IsSupported = false`; iOS audio session / ATT are no-ops | -| WebGL | ❌ Not supported | - -## Installation - -### Via Unity Package Manager (Recommended) - -1. Open Unity Package Manager (`Window` → `Package Manager`) -2. Click `+` → `Add package from git URL` -3. Enter: `https://github.com/CoderGamester/com.gamelovers.mobileservices.git` - -### Via manifest.json +## Install and configure native projects ```json { "dependencies": { - "com.gamelovers.mobileservices": "https://github.com/CoderGamester/com.gamelovers.mobileservices.git" + "com.gamelovers.mobileservices": "https://github.com/CoderGamester/Unity-MobileServices.git#1.0.1" } } ``` ---- +Before using permissions, notifications, ATT, or native UI: -## Quick Start +1. Create and commit the Mobile Services settings/config asset. +2. Fill in every required usage description and capability for the platforms you ship. +3. Decide whether the package or your project owns generated native files. +4. Validate an iOS and Android build on physical devices. -### Native UI +Without persisted configuration, the build postprocessor has no configuration to apply; it cannot infer missing privacy keys or capabilities. -```csharp -using GameLovers.MobileServices.NativeUi; - -NativeUiService.ShowAlertPopUp( - isAlertSheet: false, - title: "Delete Save?", - message: "This action cannot be undone.", - new AlertButton { Text = "Cancel", Style = AlertButtonStyle.Cancel }, - new AlertButton { Text = "Delete", Style = AlertButtonStyle.Destructive, Callback = OnDeleteConfirmed }); - -NativeUiService.ShowToastMessage("Item Collected!", isLongDuration: false); -NativeUiService.RequestReview(); // Android Play Review dependency is auto-injected at build time -NativeUiService.Share(text: "Check out my high score!", url: "https://example.com/game"); -``` +## First success -### Notifications - -```csharp -using GameLovers.MobileServices.Notifications; - -var service = new MobileNotificationService( - new GameNotificationChannel("default", "Default", "Default notifications"), - new GameNotificationChannel("rewards", "Rewards", "Daily reward reminders")); - -service.Schedule() - .In(TimeSpan.FromHours(24)) - .Title("Daily Reward Ready!") - .Body("Your daily reward is waiting for you!") - .Channel("rewards") - .BadgeIncrement() - .Send(); -``` - -### Device - -```csharp -using GameLovers.MobileServices.Device; - -IDeviceService device = new DeviceService(); - -device.Battery.OnLowPowerModeChanged += () => Debug.Log($"LPM -> {device.Battery.IsLowPowerMode}"); -device.ScreenWake.KeepAwake = true; -device.AudioSession.ConfigureForPlayback(); - -var perms = await device.Permissions.RequestAsync(AppPermission.Camera, AppPermission.Microphone); -if (perms[AppPermission.Camera] == PermissionStatus.Granted) { /* … */ } - -var att = await device.Att.RequestAuthorizationAsync(); - -device.DeepLink.OnLinkActivated += uri => Debug.Log($"Deep link: {uri}"); - -// Or with the router: -var router = new DeepLinkRouter(device.DeepLink, routes => -{ - routes.MapRoute("/promo/:id", (uri, p) => OpenPromo(p["id"])); -}); -``` - -### Haptics +Create owners during application startup and dispose them during teardown. Scheduling a notification transfers it to the operating system; disposing its service does not cancel already-scheduled OS notifications. ```csharp +using System; using GameLovers.MobileServices.Haptics; +using GameLovers.MobileServices.Notifications; +using Unity.Notifications.Android; -IHapticsService haptics = new HapticsService(); -haptics.PlayPreset(HapticPreset.Success); -haptics.PlayPresetDuration(HapticPreset.ImpactHeavy, duration: 0.5f); // auto-stop after 0.5s -haptics.PlayCustom(intensity01: 0.7f, durationMs: 250f); -haptics.StopCurrentHaptic(); -``` - -### Umbrella facade +var haptics = new HapticsService(); +haptics.Play(HapticPreset.Selection); -```csharp -using GameLovers.MobileServices; +var notifications = new MobileNotificationService( + new GameNotificationChannel("default", "Default", "General notifications")); -IMobileService mobile = new MobileService(); // bind once -mobile.NativeUi.ShowToastMessage("hi", false); -mobile.Notifications.Schedule().In(TimeSpan.FromHours(1)).Title("x").Send(); -mobile.Haptics.PlayPreset(HapticPreset.Selection); -var camera = await mobile.Device.Permissions.RequestAsync(AppPermission.Camera); +// Keep this owner and call Dispose when your application service is torn down. +IDisposable ownedNotifications = notifications; ``` ---- - -## Services at a Glance - -| Service | Purpose | -|---------|---------| -| `NativeUiService` (static) + `INativeUiService` (instance) | Alerts, sheets, toasts, review, share | -| `INotificationService` / `MobileNotificationService` | Local + remote notifications with channel registration, fluent `Schedule()` builder, and 4 `OperatingMode`s | -| `GestureController` | EnhancedTouch swipe + tap detection | -| `IHapticsService` / `HapticsService` | 9 cross-platform presets + custom intensity + time-bounded looping | -| `IDeviceService` / `DeviceService` | Umbrella over `SafeArea`, `ScreenWake`, `Battery`, `AudioSession`, `Permissions`, `Att`, `DeepLink` | -| `IDeepLinkRouter` / `DeepLinkRouter` | Path-pattern routing over `IDeepLinkService` | -| `IMobileService` / `MobileService` | Package-wide umbrella facade (NativeUi / Notifications / Haptics / Device) | -| `SafeAreaContainer` | UI Toolkit `VisualElement` that pads itself to the safe area | - -For full per-subsystem API reference, see [`docs/`](docs/README.md). - ---- - -## Editor tooling - -Runtime simulation and diagnostics live inside Unity's Device Simulator: - -- **`Window > General > Device Simulator`** — a **Mobile Services** panel appears automatically in the Control Panel. It bundles the controls (alerts / toasts / share / haptics / notifications / gestures / permissions / ATT / app review), live-state diagnostics, and a per-preset haptic envelope graph. Firing a mock paints it **inside the simulated phone screen** at the right scale and safe area, in **edit and play mode** — no second window, no platform toggle to keep in sync (the skin auto-syncs from the selected device profile). -- **Notification scheduler connection** — when the `NotificationsScheduler` sample is active in Play Mode, the panel's **Deliver next pending** action and due-time poll drive that sample's own service and paint its exact notification payload; without an active sample, **Show heads-up banner** remains a generic editor preview. -- **`EditorPlatformSimulator`** — static API for driving device / permission / ATT / deep-link state from edit-mode tests and scripted automation. - -Plus a **Mobile Services Config** asset (open via **`Tools > GameLovers > Mobile Services > Select Mobile Services Config`**) for per-permission localized usage descriptions, capability toggles, semantic iOS/Android deep-link registrations, and the auto-injection build postprocessor. The build callback resolves this persisted asset explicitly; with no persisted asset (and no temporary sample context) it performs no native mutation. - -See [`docs/explorer.md`](docs/explorer.md) and [`docs/build-pipeline.md`](docs/build-pipeline.md) for the full guide. - ---- - -## Samples - -Import the single **Mobile Services Samples** bundle from `Window > Package Manager > Mobile Services > Samples`. It is one sample with four ready-to-open UI Toolkit views. The imported bundle's single [sample README](Samples~/MobileServicesSamples/README.md) documents their shared setup and view-specific workflows. - -| Sample | Purpose | -|--------|---------| -| [Overview](Samples~/MobileServicesSamples/README.md#overview) | Native UI, permissions, device state, ATT, gestures, and safe-area wiring. | -| [Haptics](Samples~/MobileServicesSamples/README.md#haptics) | Designer iteration tool with sequence recorder + replay. | -| [Notifications](Samples~/MobileServicesSamples/README.md#notifications) | Fixed-channel scheduling/cancellation and `OperatingMode` lifecycle demo. | -| [Links](Samples~/MobileServicesSamples/README.md#links) | Route-pattern, raw-link, and cold-start replay demo. | - -Each scene opens directly from the Project window and remains independently playable. In the combined player, persistent bottom tabs navigate between **Overview**, **Haptics**, **Notifications**, and **Links**; a received OS deep link opens the Links tab automatically. Enter Play Mode before using buttons, fields, scrolling, or navigation in the Game/Simulator view; Unity 6 InputForUI routes input to UI Toolkit while the shared navigation supplies the gesture bridge, so no GameObject wiring is required. Every enabled button has visual hover/press/focus feedback and emits one `Selection` haptic when its click commits; a drag that crosses the scroll threshold cancels the button and scrolls instead. Sample ScrollViews are clamped, drag smoothly from content or controls (with a gesture fallback for simulator touch streams), and keep the bottom navigation pinned. Status cards use one `Field: Value` per line. - -The only supported player output contains all four scenes, starting on Overview. Choose **Tools > Mobile Samples Examples > Build All** to validate the imported serialized `SceneAsset` catalog, install that exact ordered scene list, and open Unity's native Build Profiles window. Choose **Restore All** in the same menu to restore the prior scene configuration during the current Unity session. The sample contributes native requirements through the shared `MobileServicesConfig` pipeline only for that exact four-scene build; the package postprocessor remains the sole native mutator. Use **Tools > Mobile Samples Examples > Verify Scene Catalog** to emit current page/path/derived-GUID identities. These sample-owned menu items and their editor bridge are never included in player builds. See [`docs/samples.md`](docs/samples.md) for setup details. - ---- - -## Related docs - -| Document | Purpose | -|---|---| -| [docs/README.md](docs/README.md) | Full API reference index | -| [docs/native-ui.md](docs/native-ui.md) | Native UI deep dive | -| [docs/notifications.md](docs/notifications.md) | Notifications deep dive (channels, modes, builder, persistence) | -| [docs/haptics.md](docs/haptics.md) | Haptics deep dive (presets, envelope, looping, backends) | -| [docs/gestures.md](docs/gestures.md) | Gesture detection deep dive | -| [docs/device.md](docs/device.md) | Device umbrella + 8 children + DeepLinkRouter | -| [docs/explorer.md](docs/explorer.md) | Device Simulator panel & in-Game-view simulator overlay | -| [docs/build-pipeline.md](docs/build-pipeline.md) | Project Settings + build postprocessor (and manual fallback) | -| [docs/samples.md](docs/samples.md) | Samples index | -| [docs/troubleshooting.md](docs/troubleshooting.md) | Symptom-to-fix table | -| [docs/superpowers/README.md](docs/superpowers/README.md) | Approved design records and implementation plans | -| [AGENTS.md](AGENTS.md) | Contributor/agent guide (architecture, gotchas, workflows) | -| [CHANGELOG.md](CHANGELOG.md) | Version history | +Use the specific subsystem namespaces—`Notifications`, `Haptics`, `NativeUi`, and `Device`—rather than assuming one umbrella import exposes every type. -## Contributing +## Services -Contributions are welcome! Report bugs or request features via [GitHub Issues](https://github.com/CoderGamester/com.gamelovers.mobileservices/issues). Include target platform (iOS/Android) and device info. For development setup, architecture, and coding standards, see [AGENTS.md](AGENTS.md). +| Area | Provides | +| --- | --- | +| Native UI | Alerts, action sheets, toasts, review requests, and sharing | +| Notifications | Local notification channels, scheduling, and management | +| Haptics | Presets, custom output, and bounded loops | +| Device | Permissions, ATT, deep links, safe-area and device helpers | +| Gestures | Gesture controller for explicit gesture input ownership | +| Editor tooling | Device Simulator integration and build helpers | -## Support +The editor simulator is for exercising application paths; it is not a substitute for device permission, notification-delivery, review, or native-build validation. -- **Issues**: [Report bugs or request features](https://github.com/CoderGamester/com.gamelovers.mobileservices/issues) -- **Discussions**: [Ask questions and share ideas](https://github.com/CoderGamester/com.gamelovers.mobileservices/discussions) +## Sample and support -## License +Import **Mobile Services Samples** from Package Manager. Its four scenes—Overview, Haptics, Notifications, and Links—share one sample player; use its [README](Samples~/MobileServicesSamples/README.md) for scene prerequisites and build tooling. -MIT — see [LICENSE.md](LICENSE.md). +See [docs](docs/README.md), [CHANGELOG.md](CHANGELOG.md), and [issues](https://github.com/CoderGamester/Unity-MobileServices/issues). diff --git a/Samples~/MobileServicesSamples/README.md b/Samples~/MobileServicesSamples/README.md index 640e4b6..f6ff9c2 100644 --- a/Samples~/MobileServicesSamples/README.md +++ b/Samples~/MobileServicesSamples/README.md @@ -1,5 +1,7 @@ # Mobile Services Samples +> **Unity compatibility:** Minimum Unity version `6000.0`; reference streams are `6000.0.x`, `6000.3.x`, and `6000.5.x`. Mobile Services is pipeline-neutral. See the [package compatibility matrix](../../README.md#unity-compatibility). + Import the single **Mobile Services Samples** entry from Package Manager. It is one sample player with four views connected by persistent bottom tabs: **Overview**, **Haptics**, **Notifications**, and **Links**. Set **Player Settings > Active Input Handling** to **Input System Package (New)** or **Both**, then open any of the four `.unity` scenes and enter Play Mode. Each scene remains independently playable, and the shared editor bridge makes tab navigation work without preparing Build Settings first. No GameObject wiring is required. Unity 6 InputForUI routes input directly to UI Toolkit, so runtime buttons, fields, scrolling, and tabs in the Game or Device Simulator view become interactive in Play Mode without a uGUI EventSystem. diff --git a/docs/README.md b/docs/README.md index b998d14..5ab32f2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,7 @@ # GameLovers Mobile Services — API Reference +> **Unity compatibility:** Minimum Unity version `6000.0`; reference streams are `6000.0.x`, `6000.3.x`, and `6000.5.x`. Mobile Services is pipeline-neutral. See the [package compatibility matrix](../README.md#unity-compatibility). + This folder contains the full API reference for each subsystem, plus the editor-side tooling docs (Device Simulator panel, Build Pipeline) that the package's `README.md` only links to. For quick-start examples see the [package README](../README.md); for architecture details and contributor guidance see [AGENTS.md](../AGENTS.md). ## Subsystems @@ -10,7 +12,7 @@ This folder contains the full API reference for each subsystem, plus the editor- | Notifications | [notifications.md](notifications.md) | | Haptics | [haptics.md](haptics.md) | | Gestures | [gestures.md](gestures.md) | -| Device (umbrella + 8 children) | [device.md](device.md) | +| Device (umbrella + 7 children, plus a separate router) | [device.md](device.md) | ## Editor tooling @@ -25,5 +27,3 @@ This folder contains the full API reference for each subsystem, plus the editor- |-------|------| | Samples index | [samples.md](samples.md) | | Troubleshooting & symptoms→fix | [troubleshooting.md](troubleshooting.md) | -| Approved design records | [superpowers/README.md](superpowers/README.md) | -| Native build ownership decision | [2026-08-09-mobile-services-native-build-ownership-design.md](superpowers/specs/2026-08-09-mobile-services-native-build-ownership-design.md) | diff --git a/package.json b/package.json index 0f657c5..9a47aad 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "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).", + "description": "Mobile platform services: local notifications, native UI, haptics, permissions, ATT, deep links, gestures, device helpers, and build/simulator tooling.", "type": "library", "dependencies": { "com.unity.mobile.notifications": "2.3.0", From 4ac7a2ccf39aacf2335a28320a63772ef3ec527c Mon Sep 17 00:00:00 2001 From: CoderGamester Date: Thu, 13 Aug 2026 16:18:18 +0300 Subject: [PATCH 2/5] refactor: move keep awake to DeviceService --- AGENTS.md | 4 +- CHANGELOG.md | 31 +++++----- Runtime/Device/DeviceService.cs | 23 ++++--- Runtime/Device/IDeviceService.cs | 10 ++- Runtime/Device/State/IScreenWakeService.cs | 15 ----- .../Device/State/IScreenWakeService.cs.meta | 2 - Runtime/Device/State/ScreenWakeService.cs | 16 ----- .../Device/State/ScreenWakeService.cs.meta | 2 - Runtime/IMobileService.cs | 2 +- .../MobileServicesPlaygroundUI.cs | 6 +- Tests/AGENTS.md | 2 +- Tests/EditMode/Unit/DeviceServiceTest.cs | 50 ++++++++++++--- Tests/EditMode/Unit/ScreenWakeServiceTest.cs | 61 ------------------- .../Unit/ScreenWakeServiceTest.cs.meta | 2 - .../Unit/DeviceServicePlayModeTest.cs | 1 - docs/device.md | 16 ++++- 16 files changed, 98 insertions(+), 145 deletions(-) delete mode 100644 Runtime/Device/State/IScreenWakeService.cs delete mode 100644 Runtime/Device/State/IScreenWakeService.cs.meta delete mode 100644 Runtime/Device/State/ScreenWakeService.cs delete mode 100644 Runtime/Device/State/ScreenWakeService.cs.meta delete mode 100644 Tests/EditMode/Unit/ScreenWakeServiceTest.cs delete mode 100644 Tests/EditMode/Unit/ScreenWakeServiceTest.cs.meta diff --git a/AGENTS.md b/AGENTS.md index 1ef1496..97473c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,7 +15,7 @@ This package consolidates mobile-specific platform services: - **Notifications**: platform wrapper over Unity Mobile Notifications (Android/iOS) with a fluent `service.Schedule().In(...).Title(...).Send()` builder (`NotificationBuilder`). - **Gestures**: Input System–based pointer abstraction + swipe/tap detection. - **Haptics**: zero-dependency haptic feedback with 9 presets, custom intensity, time-bounded looping. Built directly on iOS `UI*FeedbackGenerator` + Android `VibrationEffect.createWaveform` — no NiceVibrations or other third-party plugin. -- **Device**: `IDeviceService` umbrella facade over 7 sub-services — `SafeArea`, `ScreenWake`, `Battery` (with iOS / Android low-power-mode awareness), `AudioSession` (iOS silent-switch override), `Permissions` (unified iOS+Android, Task-based async, including the multi-permission `RequestAsync(params AppPermission[])` overload), `Att` (App Tracking Transparency, no `com.unity.ads.ios-support` dep), `DeepLink` (with cold-start link queueing) — plus an `IDeepLinkRouter` layered on `IDeepLinkService` for path-pattern routing. +- **Device**: `IDeviceService` umbrella facade over 6 sub-services — `SafeArea`, `Battery` (with iOS / Android low-power-mode awareness), `AudioSession` (iOS silent-switch override), `Permissions` (unified iOS+Android, Task-based async, including the multi-permission `RequestAsync(params AppPermission[])` overload), `Att` (App Tracking Transparency, no `com.unity.ads.ios-support` dep), `DeepLink` (with cold-start link queueing) — plus static `DeviceService.KeepAwake` sugar over `Screen.sleepTimeout` and an `IDeepLinkRouter` layered on `IDeepLinkService` for path-pattern routing. - **`IMobileService`** umbrella facade aggregating `NativeUi` / `Notifications` / `Haptics` / `Device` behind a single DI registration. For user-facing docs, treat `README.md` as the primary entry point — it's the lean overview. Deeper per-subsystem API reference lives in [`docs/`](docs/) (`docs/README.md` is the index). This file is for contributors/agents working on the package itself. @@ -57,7 +57,7 @@ This namespace holds the umbrella facade plus every device-touching service. All - **Shared host**: `Runtime/Device/Internal/DeviceServicesHost.cs` — internal `MonoBehaviour`, `DontDestroyOnLoad`, lazily spawned. Exposes `RegisterLateUpdate` / `RegisterSecondTick` / `RegisterFocusChanged` / `RegisterIosLowPowerModeChanged`. Means the runtime cost of the entire Device subsystem is a single GameObject. - **Audio Session**: `Runtime/Device/Audio/IIosAudioSessionService.cs` + `IosAudioSessionService.cs`. `ConfigureForPlayback()` sets `AVAudioSessionCategoryPlayback` + `setActive:YES` via `Plugins/iOS/iOSAudioSession.m`. Android / Editor / unsupported platforms are safe no-ops. Instance (not static) so it can sit on `IDeviceService.AudioSession`. - **Safe Area**: `Runtime/Device/State/ISafeAreaService.cs` + `SafeAreaService.cs`. Polls `Screen.safeArea` in `LateUpdate` via the host; fires `OnSafeAreaChanged` on diff. Companion `SafeAreaContainer` UI Toolkit `VisualElement` self-pads to the safe area; can be constructed with the service or wired up via `SetSafeAreaService` for UXML usage. -- **Screen Wake**: `Runtime/Device/State/IScreenWakeService.cs` + `ScreenWakeService.cs`. Trivial wrapper over `Screen.sleepTimeout`; idempotent. +- **Screen Wake**: `DeviceService.KeepAwake` is static sugar over `Screen.sleepTimeout`; it is deliberately outside the injectable child graph because it holds no state or platform abstraction. - **Battery**: `Runtime/Device/State/IBatteryService.cs` + `BatteryService.cs`. Polls `SystemInfo.batteryLevel` / `batteryStatus` once per second via the host; fires `OnLevelChanged` (≥1% diff), `OnStatusChanged`, `OnLowPowerModeChanged`. iOS LPM via `Plugins/iOS/Battery.m` exposing `_GameLoversBatteryIsLowPowerModeEnabled` plus an `NSProcessInfoPowerStateDidChangeNotification` observer that calls back via `UnitySendMessage("DeviceServicesHost", "OnIosLowPowerModeChanged", "")`. Android LPM polled via JNI `PowerManager.isPowerSaveMode()` on focus change. - **Permissions**: `Runtime/Device/Permissions/IPermissionsService.cs` + `PermissionsService.cs`. `Check(...)` is sync, `RequestAsync(...)` returns `Task` (no UniTask dep). Android uses `UnityEngine.Android.Permission` with manifest mapping for Camera/Mic/FineLocation; uses `READ_MEDIA_IMAGES` (API 33+) for Photos and `POST_NOTIFICATIONS` for Notifications. iOS uses `Plugins/iOS/Permissions.m` with one bridge per permission (`AVCaptureDevice` for Camera/Mic, `CLLocationManager` for Location, `PHPhotoLibrary` for Photos, `UNUserNotificationCenter` for Notifications). Async results returned via `UnitySendMessage("PermissionsCallbackReceiver", "OnPermissionResult", ":")` to `Runtime/Device/Permissions/Internal/PermissionsCallbackReceiver.cs` which resolves the matching `TaskCompletionSource`. - **Location delegate lifetime**: iOS bridge keeps `CLLocationManager` instances alive in a static `NSMutableArray` so the delegate isn't GC'd before `locationManagerDidChangeAuthorization:` fires. The delegate clears itself from the manager after dispatch. diff --git a/CHANGELOG.md b/CHANGELOG.md index b504d97..e94345b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ 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). - -## [Unreleased] - -**Changed**: -- Declared Unity 6000.0 as the package minimum and documented 6000.0.x, 6000.3.x, and 6000.5.x as compatibility reference streams. +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +**Changed**: +- Declared Unity 6000.0 as the package minimum and documented 6000.0.x, 6000.3.x, and 6000.5.x as compatibility reference streams. +- Moved the keep-awake convenience API to the static `DeviceService.KeepAwake` property and removed the redundant `IScreenWakeService` / `ScreenWakeService` child service. ## [1.0.1] - 2026-08-12 @@ -34,17 +35,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Device Simulator Plugin**: An embedded Unity Device Simulator panel provides platform-shaped native UI mocks, live diagnostics, and a per-preset haptic envelope graph. - **Mobile Services Config asset**: Configure localized permission descriptions, capability toggles, Android manifest opt-ins, and Play In-App Review Gradle setup from `Tools > GameLovers > Mobile Services > Select Mobile Services Config`. - **Build Postprocessor**: Automatically inject iOS usage descriptions and entitlements, Android manifest entries, and the Play In-App Review Gradle dependency, with validation for missing configuration. -- **Samples**: Added one importable **Mobile Services Samples** bundle containing independently playable Playground, Haptics Palette, Notifications Scheduler, and Deep Link Router scenes. +- **Samples**: Added one importable **Mobile Services Samples** bundle containing independently playable Playground, Haptics Palette, Notifications Scheduler, and Deep Link Router scenes. - **Documentation**: Added subsystem references and editor-tooling guides for the Device Simulator and build pipeline. -**Changed**: -- Consolidated the package under the `com.gamelovers.mobileservices` package name, `GameLovers.MobileServices.*` namespaces, and `GameLovers.MobileServices` assembly. -- Updated the package baseline to Unity 6 and documented the supported 6000.5.7f1, 6000.3.21f1, and 6000.0.81f1 validation editors. - -**Fixed**: -- Fixed persisted notifications so nullable IDs, badge numbers, and delivery times survive background/foreground rescheduling. -- Fixed local notification delivered and expired events so subscribers added after service construction receive callbacks. -- Fixed editor notification scheduling so generated notifications appear in the pending collection. +**Changed**: +- Consolidated the package under the `com.gamelovers.mobileservices` package name, `GameLovers.MobileServices.*` namespaces, and `GameLovers.MobileServices` assembly. +- Updated the package baseline to Unity 6 and documented the supported 6000.5.7f1, 6000.3.21f1, and 6000.0.81f1 validation editors. + +**Fixed**: +- Fixed persisted notifications so nullable IDs, badge numbers, and delivery times survive background/foreground rescheduling. +- Fixed local notification delivered and expired events so subscribers added after service construction receive callbacks. +- Fixed editor notification scheduling so generated notifications appear in the pending collection. **Removed**: - Removed legacy tap detection; use the Unity Input System's `TapInteraction` instead. diff --git a/Runtime/Device/DeviceService.cs b/Runtime/Device/DeviceService.cs index e04ae7e..3010966 100644 --- a/Runtime/Device/DeviceService.cs +++ b/Runtime/Device/DeviceService.cs @@ -1,5 +1,6 @@ using System; using GameLovers.MobileServices.Device.Internal; +using UnityEngine; // ReSharper disable once CheckNamespace namespace GameLovers.MobileServices.Device @@ -7,11 +8,20 @@ namespace GameLovers.MobileServices.Device /// public sealed class DeviceService : IDeviceService, IDisposable { + /// + /// Controls whether the device screen stays awake. When true, sets + /// Screen.sleepTimeout to SleepTimeout.NeverSleep; when false, + /// restores SleepTimeout.SystemSetting. + /// + public static bool KeepAwake + { + get => Screen.sleepTimeout == SleepTimeout.NeverSleep; + set => Screen.sleepTimeout = value ? SleepTimeout.NeverSleep : SleepTimeout.SystemSetting; + } + /// public ISafeAreaService SafeArea { get; } /// - public IScreenWakeService ScreenWake { get; } - /// public IBatteryService Battery { get; } /// public IIosAudioSessionService AudioSession { get; } @@ -26,7 +36,6 @@ public DeviceService() : this(BuildDefaults()) { } public DeviceService( ISafeAreaService safeArea, - IScreenWakeService screenWake, IBatteryService battery, IIosAudioSessionService audioSession, IPermissionsService permissions, @@ -34,7 +43,6 @@ public DeviceService( IDeepLinkService deepLink) { SafeArea = safeArea; - ScreenWake = screenWake; Battery = battery; AudioSession = audioSession; Permissions = permissions; @@ -45,20 +53,19 @@ public DeviceService( // Tuple-routed delegating ctor so the host-dependent children share one explicit host // instance constructed up-front, not separate accesses to the singleton during a // constructor chain (cleaner ownership signal in the umbrella's call stack). - private DeviceService((ISafeAreaService, IScreenWakeService, IBatteryService, + private DeviceService((ISafeAreaService, IBatteryService, IIosAudioSessionService, IPermissionsService, IAttService, IDeepLinkService) defaults) : this(defaults.Item1, defaults.Item2, defaults.Item3, - defaults.Item4, defaults.Item5, defaults.Item6, defaults.Item7) + defaults.Item4, defaults.Item5, defaults.Item6) { } - private static (ISafeAreaService, IScreenWakeService, IBatteryService, + private static (ISafeAreaService, IBatteryService, IIosAudioSessionService, IPermissionsService, IAttService, IDeepLinkService) BuildDefaults() { var host = DeviceServicesHost.Instance; return ( new SafeAreaService(host), - new ScreenWakeService(), new BatteryService(host), new IosAudioSessionService(), new PermissionsService(), diff --git a/Runtime/Device/IDeviceService.cs b/Runtime/Device/IDeviceService.cs index aeb05b9..436a841 100644 --- a/Runtime/Device/IDeviceService.cs +++ b/Runtime/Device/IDeviceService.cs @@ -2,18 +2,16 @@ namespace GameLovers.MobileServices.Device { /// - /// Umbrella facade aggregating every device-touching service in the package. Use as a single - /// DI registration to expose the full Device subsystem; each child interface is also - /// independently registerable for testing/mocking. + /// Umbrella facade aggregating the stateful, injectable device services in the package. Use as + /// a single DI registration to expose the Device subsystem; each child interface is also + /// independently registerable for testing/mocking. Stateless global conveniences are exposed + /// directly by . /// public interface IDeviceService { /// Display safe-area events (notch, dynamic island, orientation). ISafeAreaService SafeArea { get; } - /// Toggle Screen.sleepTimeout (keep the screen awake). - IScreenWakeService ScreenWake { get; } - /// Battery level / status / low-power-mode awareness. IBatteryService Battery { get; } diff --git a/Runtime/Device/State/IScreenWakeService.cs b/Runtime/Device/State/IScreenWakeService.cs deleted file mode 100644 index d5429c6..0000000 --- a/Runtime/Device/State/IScreenWakeService.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace GameLovers.MobileServices.Device -{ - /// - /// Controls whether the device screen should stay awake (override the OS sleep timeout). - /// - public interface IScreenWakeService - { - /// - /// When true, sets Screen.sleepTimeout to SleepTimeout.NeverSleep; - /// when false, restores SleepTimeout.SystemSetting. Idempotent. - /// - bool KeepAwake { get; set; } - } -} diff --git a/Runtime/Device/State/IScreenWakeService.cs.meta b/Runtime/Device/State/IScreenWakeService.cs.meta deleted file mode 100644 index 08650cf..0000000 --- a/Runtime/Device/State/IScreenWakeService.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: bcda1d4b4808e4808ba9b6fd7e980d99 \ No newline at end of file diff --git a/Runtime/Device/State/ScreenWakeService.cs b/Runtime/Device/State/ScreenWakeService.cs deleted file mode 100644 index e6ed370..0000000 --- a/Runtime/Device/State/ScreenWakeService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UnityEngine; - -// ReSharper disable once CheckNamespace -namespace GameLovers.MobileServices.Device -{ - /// - public sealed class ScreenWakeService : IScreenWakeService - { - /// - public bool KeepAwake - { - get => Screen.sleepTimeout == SleepTimeout.NeverSleep; - set => Screen.sleepTimeout = value ? SleepTimeout.NeverSleep : SleepTimeout.SystemSetting; - } - } -} diff --git a/Runtime/Device/State/ScreenWakeService.cs.meta b/Runtime/Device/State/ScreenWakeService.cs.meta deleted file mode 100644 index 88e2b1b..0000000 --- a/Runtime/Device/State/ScreenWakeService.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: f4ef98007e9d54bd2acaddde0c165c80 \ No newline at end of file diff --git a/Runtime/IMobileService.cs b/Runtime/IMobileService.cs index 4be099e..cd3eada 100644 --- a/Runtime/IMobileService.cs +++ b/Runtime/IMobileService.cs @@ -28,7 +28,7 @@ public interface IMobileService IHapticsService Haptics { get; } /// - /// Device sub-services (safe area, screen wake, battery, audio session, permissions, ATT, deep link). + /// Device sub-services (safe area, battery, audio session, permissions, ATT, deep link). /// IDeviceService Device { get; } } diff --git a/Samples~/MobileServicesSamples/MobileServicesPlayground/MobileServicesPlaygroundUI.cs b/Samples~/MobileServicesSamples/MobileServicesPlayground/MobileServicesPlaygroundUI.cs index c5f6e83..60f1b6f 100644 --- a/Samples~/MobileServicesSamples/MobileServicesPlayground/MobileServicesPlaygroundUI.cs +++ b/Samples~/MobileServicesSamples/MobileServicesPlayground/MobileServicesPlaygroundUI.cs @@ -52,7 +52,7 @@ private void Update() new SampleStatusEntry("Battery level", batteryLevel), new SampleStatusEntry("Battery status", _device.Battery.Status), new SampleStatusEntry("Low-power mode", SampleStatusFormatter.YesNo(_device.Battery.IsLowPowerMode)), - new SampleStatusEntry("Keep awake", SampleStatusFormatter.YesNo(_device.ScreenWake.KeepAwake)), + new SampleStatusEntry("Keep awake", SampleStatusFormatter.YesNo(DeviceService.KeepAwake)), new SampleStatusEntry("ATT status", _device.Att.CurrentStatus)); } if (_safeAreaStatus != null) @@ -149,8 +149,8 @@ private void BindButtons(VisualElement root) }); root.Q