One of the challenges of web development is to keep the screen awake when an application needs to keep running. For example, if you are reading an ebook, following a recipe, or scanning a QR code, you don't want the screen to dim or lock automatically. This can be annoying for the user and affect the usability of your app.
The Screen Wake Lock API provides a way to prevent devices from dimming or locking the screen when an application needs to keep running. It is a simple and platform-independent solution that works on any modern browser that supports Web APIs.
In this article, I will show you how to use the Screen Wake Lock API in a Blazor application using the JavaScript interop feature.
Prerequisites
To follow this tutorial, you will need:
- Visual Studio 2022 or Visual Studio Code with the C# extension
- .NET 7 SDK
Creating a Blazor app
To create a Blazor app, you can use the Blazor WebAssembly App template in Visual Studio or run the following command in a terminal:
dotnet new blazorwasm -o ScreenWakeLockDemo
This will create a Blazor WebAssembly app with a default layout and some sample pages. You can run the app by pressing F5 in Visual Studio or typing dotnet run
in the terminal.
Adding the Screen Wake Lock API
To use the Screen Wake Lock API, you need to access the navigator.wakeLock
property from JavaScript. This property returns a WakeLock
object that allows you to request and release a wake lock of type screen
.
To call JavaScript functions from C#, you can use the IJSRuntime
service that is injected into your Blazor components. You can also create a C# wrapper class that encapsulates the JavaScript interop logic and exposes a simple and type-safe API.
In this example, I will create a ScreenWakeLockService
class that implements the following interface:
public interface IScreenWakeLockService
{
// Requests a screen wake lock and returns a sentinel object
Task<WakeLockSentinel> RequestWakeLockAsync();
// Releases a screen wake lock given a sentinel object
Task ReleaseWakeLockAsync(WakeLockSentinel sentinel);
// Checks if the browser supports the screen wake lock API
Task<bool> IsSupportedAsync();
}
The WakeLockSentinel
class is a simple wrapper around a JavaScript object reference that represents the underlying platform wake lock. It has an Id
property that is used to identify and release the wake lock.
public class WakeLockSentinel
{
public WakeLockSentinel(int id, IJSObjectReference jsObjectReference)
{
Id = id;
JsObjectReference = jsObjectReference;
}
public int Id { get; }
public IJSObjectReference JsObjectReference { get; }
}
The implementation of the ScreenWakeLockService
class is as follows:
public class ScreenWakeLockService : IScreenWakeLockService
{
private readonly IJSRuntime _jsRuntime;
private readonly ConcurrentDictionary<int, WakeLockSentinel> _wakeLocks;
private int _nextId;
public ScreenWakeLockService(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
_wakeLocks = new ConcurrentDictionary<int, WakeLockSentinel>();
_nextId = 0;
}
public async Task<WakeLockSentinel> RequestWakeLockAsync()
{
// Check if the browser supports the screen wake lock API
var isSupported = await IsSupportedAsync();
if (!isSupported)
{
throw new NotSupportedException("The browser does not support the screen wake lock API.");
}
// Request a screen wake lock and get a JS object reference
var jsObjectReference = await _jsRuntime.InvokeAsync<IJSObjectReference>("navigator.wakeLock.request", "screen");
// Create a sentinel object and store it in a dictionary
var id = Interlocked.Increment(ref _nextId);
var sentinel = new WakeLockSentinel(id, jsObjectReference);
_wakeLocks.TryAdd(id, sentinel);
// Return the sentinel object
return sentinel;
}
public async Task ReleaseWakeLockAsync(WakeLockSentinel sentinel)
{
// Check if the sentinel object is valid
if (sentinel == null || sentinel.JsObjectReference == null)
{
throw new ArgumentNullException(nameof(sentinel));
}
// Release the screen wake lock and dispose the JS object reference
await sentinel.JsObjectReference.InvokeVoidAsync("release");
await sentinel.JsObjectReference.DisposeAsync();
// Remove the sentinel object from the dictionary
_wakeLocks.TryRemove(sentinel.Id, out _);
}
public async Task<bool> IsSupportedAsync()
{
// Check if the navigator.wakeLock property exists
return await _jsRuntime.InvokeAsync<bool>("eval", "typeof navigator.wakeLock !== 'undefined'");
}
}
The RequestWakeLockAsync
method checks if the browser supports the screen wake lock API and then invokes the navigator.wakeLock.request
function with the "screen"
argument. This returns a promise that resolves to a WakeLockSentinel
object in JavaScript, which is wrapped by a IJSObjectReference
in C#. The method then creates a WakeLockSentinel
instance with a unique id and stores it in a concurrent dictionary for later use.
The ReleaseWakeLockAsync
method takes a WakeLockSentinel
instance as an argument and invokes the release
method on the corresponding JavaScript object. It then disposes the IJSObjectReference
and removes the sentinel from the dictionary.
The IsSupportedAsync
method simply checks if the navigator.wakeLock
property exists in JavaScript and returns a boolean value.
To use this service in your Blazor components, you need to register it as a singleton in the Program.cs
file:
builder.Services.AddSingleton<IScreenWakeLockService, ScreenWakeLockService>();
Using the Screen Wake Lock Service
Now that you have created a screen wake lock service, you can use it in your Blazor components to request and release a screen wake lock. For example, you can create a simple component that has a toggle button to enable or disable the screen wake lock:
@page "/screenwakelock"
@inject IScreenWakeLockService ScreenWakeLockService
<h1>Screen Wake Lock Demo</h1>
@if (_isSupported)
{
<p>The browser supports the screen wake lock API.</p>
<button @onclick="ToggleWakeLock">@(_isLocked ? "Disable" : "Enable") screen wake lock</button>
}
else
{
<p>The browser does not support the screen wake lock API.</p>
}
@code {
private bool _isSupported;
private bool _isLocked;
private WakeLockSentinel _sentinel;
protected override async Task OnInitializedAsync()
{
// Check if the browser supports the screen wake lock API
_isSupported = await ScreenWakeLockService.IsSupportedAsync();
}
private async Task ToggleWakeLock()
{
try
{
if (_isLocked)
{
// Release the screen wake lock
await ScreenWakeLockService.ReleaseWakeLockAsync(_sentinel);
_sentinel = null;
_isLocked = false;
}
else
{
// Request a screen wake lock
_sentinel = await ScreenWakeLockService.RequestWakeLockAsync();
_isLocked = true;
}
}
catch (Exception ex)
{
// Handle possible errors or rejections
Console.WriteLine(ex.Message);
}
}
}
This component injects the IScreenWakeLockService
into a field and calls its methods in the OnInitializedAsync
and ToggleWakeLock
methods. It also handles possible errors or rejections that may occur when requesting or releasing a screen wake lock.
Use cases for the Screen Wake Lock API
The Screen Wake Lock API can be useful for any web app that needs to keep running while the user is not interacting with it. Some examples are:
- Reading apps that display long texts or ebooks
- Cooking apps that show recipes or timers
- Presentation apps that show slides or videos
- Gaming apps that use device motion or voice control
- Scanning apps that use QR codes or barcodes
- Fitness apps that track workouts or heart rate
Conclusion
In this article, I showed you how to prevent the screen turn off in a Blazor application using the Screen Wake Lock API with the JavaScript interop feature. I will also explain some use cases for the screen wake lock API and how to handle possible errors and rejections.
Top comments (1)
Very informative, thank you