Skip to content

Commit 596c442

Browse files
committed
feat: Add platform services
1 parent f39f50e commit 596c442

27 files changed

+1082
-21
lines changed

DeviceCore.sln

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36301.6
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{AD02B423-808C-484C-B74C-8BD7D1A4A6F8}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCore.Abstractions", "src\lib\DeviceCore.Abstractions\DeviceCore.Abstractions.csproj", "{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCore.Uno.WinUI", "src\lib\DeviceCore.Uno.WinUI\DeviceCore.Uno.WinUI.csproj", "{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}"
13+
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{FA423D9C-B957-4517-9D09-0BF8E8991008}"
15+
ProjectSection(SolutionItems) = preProject
16+
build\azure-pipelines.yml = build\azure-pipelines.yml
17+
build\gitversion.yml = build\gitversion.yml
18+
build\stage-build.yml = build\stage-build.yml
19+
build\stage-release.yml = build\stage-release.yml
20+
EndProjectSection
21+
EndProject
22+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{FD0DD8AF-C1A2-4FB4-9FE3-D039FA20D310}"
23+
ProjectSection(SolutionItems) = preProject
24+
.gitignore = .gitignore
25+
.mergify.yml = .mergify.yml
26+
BREAKING_CHANGES.md = BREAKING_CHANGES.md
27+
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
28+
CONTRIBUTING.md = CONTRIBUTING.md
29+
LICENSE = LICENSE
30+
README.md = README.md
31+
EndProjectSection
32+
EndProject
33+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{DCAB9D57-69DC-4C5D-8703-43C374CB0387}"
34+
EndProject
35+
Global
36+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
37+
Debug|Any CPU = Debug|Any CPU
38+
Release|Any CPU = Release|Any CPU
39+
EndGlobalSection
40+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
41+
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42+
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
43+
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
44+
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Release|Any CPU.Build.0 = Release|Any CPU
45+
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46+
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Debug|Any CPU.Build.0 = Debug|Any CPU
47+
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Release|Any CPU.ActiveCfg = Release|Any CPU
48+
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Release|Any CPU.Build.0 = Release|Any CPU
49+
EndGlobalSection
50+
GlobalSection(SolutionProperties) = preSolution
51+
HideSolutionNode = FALSE
52+
EndGlobalSection
53+
GlobalSection(NestedProjects) = preSolution
54+
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
55+
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
56+
EndGlobalSection
57+
GlobalSection(ExtensibilityGlobals) = postSolution
58+
SolutionGuid = {483215CF-95DE-4CCF-A682-832BB70A63F4}
59+
EndGlobalSection
60+
EndGlobal

README.md

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,116 @@
1-
# Open Source Project Template
1+
# Device Core
22

3-
This repository contains a template to seed a repository for an Open Source
4-
project.
3+
{Project tag line}
54

6-
## How to use this template
5+
{Small description of the purpose of the project}
76

8-
1. Check out this repository
9-
2. Delete the `.git` folder
10-
3. Git init this repository and start working on your project!
11-
4. Prior to submitting your request for publication, make sure to review the
12-
[Open Source guidelines for publications](https://nventive.visualstudio.com/Internal/_wiki/wikis/Internal_wiki?wikiVersion=GBwikiMaster&pagePath=%2FOpen%20Source%2FPublishing&pageId=7120).
7+
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
138

14-
## Features (to keep as-is, configure or remove)
15-
- [Mergify](https://mergify.io/) is configured. You can edit or remove [.mergify.yml](/.mergify.yml).
9+
## Getting Started
1610

17-
The following is the template for the final README.md file:
11+
1. Add the `DeviceCore.Uno.WinUI` NuGet package to your projects (Windows, Android and iOS).
12+
> 💡 If you need to implement more platforms or create custom implementations, you can use the `DeviceCore.Abstractions` NuGet package.
1813
19-
---
14+
1. Create an instance of any service. We'll cover dependency injection in details later on in this documentation.
2015

21-
# Project Title
16+
```cs
17+
using DeviceCore;
2218

23-
{Project tag line}
19+
var flashlightService = new FlashlightService();
20+
```
2421

25-
{Small description of the purpose of the project}
22+
1. Use the service.
2623

27-
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
24+
```cs
25+
flashlightService.Brightness = 0.5f;
26+
flashlightService.Toggle();
27+
```
2828

29-
## Getting Started
29+
## Next Step
30+
31+
### Using Dependency Injection
32+
33+
Here is a simple code that does dependency injection using `Microsoft.Extensions.DependencyInjection` and `Microsoft.Extensions.Hosting`.
3034

31-
{Instructions to quickly get started using the project: pre-requisites, packages
32-
to install, sample code, etc.}
35+
```cs
36+
using DeviceCore;
37+
using Microsoft.Extensions.DependencyInjection;
38+
using Microsoft.Extensions.Hosting;
39+
40+
var host = new HostBuilder()
41+
.ConfigureServices(serviceCollection => serviceCollection
42+
.AddSingleton(_ => DispatcherQueue.GetForCurrentThread())
43+
.AddSingleton<IAccelerometerService, AccelerometerService>()
44+
.AddSingleton<IAmbientLightProvider, AmbientLightProvider>()
45+
.AddSingleton<IBatteryInformationProvider, BatteryInformationProvider>()
46+
.AddSingleton<IFlashlightService, FlashlightService>()
47+
.AddSingleton<IScreenWakeLockService, ScreenWakeLockService>()
48+
)
49+
.Build();
50+
```
3351

3452
## Features
3553

36-
{More details/listing of features of the project}
54+
Now that everything is setup, Let's see what else we can do!
55+
56+
### Accelerometer
57+
58+
The `AccelerometerService` provides access to the device's accelerometer sensor, allowing you to read acceleration data in three dimensions (X, Y, Z).
59+
60+
> 💡 The `ObserveAcceleration` and `ObserveDeviceShaken` methods and will return an observable yielding `null` on devices that do not support `Accelerometer` or devices that do not have such a sensor.
61+
62+
> 💡 Unsubscribe from the `ObserveAcceleration` and `ObserveDeviceShaken` observables when you no longer need the readings to avoid unnecessary battery consumption.
63+
64+
> 💡 On iOS features built-in shake gesture recognition. Android use a common implementation to approximate shake detection. You can implement custom shake detection using the `ObserveAcceleration` method if needed.
65+
66+
> 💡 On Android, if both `ObserveAcceleration` and `ObserveDeviceShaken` observables are used and `ReportInterval` is set high, they may be yielding reading more often than requested due to multiple subscribers.
67+
68+
### Light Sensor
69+
70+
The `AmbientLightProvider` provides access to the device's ambient light sensor, allowing you to read the current light level.
71+
72+
> 💡 The `ObserveCurrentReading` method and will return an observable yielding `null` on devices that do not support `Accelerometer`, on devices that do not have such a sensor, or on iOS.
73+
74+
> 💡 Unsubscribe from the `ObserveCurrentReading` observable when you no longer need the readings to avoid unnecessary battery consumption.
75+
76+
### Battery Information
77+
78+
The `BatteryInformationProvider` provides access to the device's battery information, allowing you to read the current battery level and status.
79+
80+
> 💡 On Android, the `GetAndObserveRemainingChargePercent` observable is not updated continuously as there is no API that provides such events. It is triggered by system `Low` and `Ok` battery state broadcasts only. The `RemainingChargePercent` property always returns the up-to-date value. For continuous monitoring you can set up periodic polling.
81+
82+
#### Android
83+
84+
To use the battery information on Android, ensure you have the correct permissions in your `AndroidManifest.xml`.
85+
86+
```xml
87+
<uses-permission android:name="android.permission.BATTERY_STATS" />
88+
```
89+
90+
### Flashlight
91+
92+
The `FlashlightService` allows you to turn the phone's camera flashlight on and off.
93+
94+
> 💡 On Android, flashlight brightness cannot be controlled, hence any non-zero brightness level results in the full brightness of the flashlight.
95+
96+
> 💡 On iOS, in case the device supports the torch, brightness level is fully supported. In case the device has only flash, any non-zero brightness level will result in the full brightness of the flashlight.
97+
98+
#### Android
99+
100+
To use the flashlight on Android, ensure you have the correct permissions in your `AndroidManifest.xml`.
101+
102+
```xml
103+
<uses-permission android:name="android.permission.FLASHLIGHT" />
104+
<uses-permission android:name="android.permission.CAMERA" />
105+
```
106+
107+
### Keeping Screen On
108+
109+
To enables an application to request to keep the device's screen on, use the `ScreenWakeLockService`.
110+
111+
## Acknowledgements
112+
113+
Take a look at [Uno.WinRT](https://platform.uno/docs/articles/features/using-winrt.html) that we use for the mobile platforms implementation.
37114

38115
## Breaking Changes
39116

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace DeviceCore;
5+
6+
/// <summary>
7+
/// Represents an accelerometer reading.
8+
/// </summary>
9+
public sealed class AccelerometerReading
10+
{
11+
public AccelerometerReading(double accelerationX, double accelerationY, double accelerationZ, TimeSpan? performanceCount, DateTimeOffset timestamp)
12+
{
13+
AccelerationX = accelerationX;
14+
AccelerationY = accelerationY;
15+
AccelerationZ = accelerationZ;
16+
PerformanceCount = performanceCount;
17+
Timestamp = timestamp;
18+
}
19+
20+
/// <summary>
21+
/// Gets the g-force acceleration along the x-axis.
22+
/// </summary>
23+
public double AccelerationX { get; }
24+
25+
/// <summary>
26+
/// Gets the g-force acceleration along the y-axis.
27+
/// </summary>
28+
public double AccelerationY { get; }
29+
30+
/// <summary>
31+
/// Gets the g-force acceleration along the z-axis.
32+
/// </summary>
33+
public double AccelerationZ { get; }
34+
35+
/// <summary>
36+
/// Gets the performance count associated with the reading.
37+
/// This allows the reading to be synchronized with other devices and processes on the system.
38+
/// </summary>
39+
public TimeSpan? PerformanceCount { get; }
40+
41+
/// <summary>
42+
/// Gets the data properties reported by the sensor.
43+
/// </summary>
44+
public IReadOnlyDictionary<string, object>? Properties { get; }
45+
46+
/// <summary>
47+
/// Gets the time at which the sensor reported the reading.
48+
/// </summary>
49+
public DateTimeOffset Timestamp { get; }
50+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Reactive.Linq;
3+
4+
namespace DeviceCore;
5+
6+
/// <summary>
7+
/// The fake implementation of <see cref="IAccelerometerService"/> for testing purposes.
8+
/// </summary>
9+
public sealed class FakeAccelerometerService : IAccelerometerService
10+
{
11+
/// <inheritdoc/>
12+
public uint ReportInterval { get; set; }
13+
14+
/// <inheritdoc/>
15+
public IObservable<AccelerometerReading?> ObserveAcceleration()
16+
{
17+
return Observable.Return<AccelerometerReading?>(new AccelerometerReading(0d, 0d, 0d, null, DateTimeOffset.Now));
18+
}
19+
20+
/// <inheritdoc/>
21+
public IObservable<DateTimeOffset?> ObserveDeviceShaken()
22+
{
23+
return Observable.Return<DateTimeOffset?>(DateTimeOffset.Now);
24+
}
25+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
3+
namespace DeviceCore;
4+
5+
/// <summary>
6+
/// Provides functionality to observe accelerometer readings and detect device shake events.
7+
/// </summary>
8+
/// <remarks>
9+
/// On Android, when both ReadingChanged and Shaken events are attached and the user sets the ReportInterval to a high value, the ReadingChanged event may be raised more often than requested.
10+
/// This is because for multiple subscribers to the same sensor, the system may raise the sensor events with the frequency of the one with the lowest requested report delay.
11+
/// This is, however, in line with the behavior of the WinUI Accelerometer, and you can filter the events as necessary for your use case.
12+
/// </remarks>
13+
public interface IAccelerometerService
14+
{
15+
/// <summary>
16+
/// Gets or sets the current report interval for the accelerometer.
17+
/// </summary>
18+
uint ReportInterval { get; set; }
19+
20+
/// <summary>
21+
/// Observes the acceleration data from the device's accelerometer.
22+
/// </summary>
23+
/// <remarks>
24+
/// If the device does not support an accelerometer sensor, this method will return an observable sequence that yields null.
25+
/// </remarks>
26+
IObservable<AccelerometerReading?> ObserveAcceleration();
27+
28+
/// <summary>
29+
/// Observes when the device is shaken.
30+
/// </summary>
31+
/// <remarks>
32+
/// If the device does not support an accelerometer sensor, this method will return an observable sequence that yields null.
33+
/// </remarks>
34+
/// <returns>An observable sequence yielding a timestamp when the device has been shaken.</returns>
35+
IObservable<DateTimeOffset?> ObserveDeviceShaken();
36+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace DeviceCore;
5+
6+
/// <summary>
7+
/// Represents an ambient light–sensor reading.
8+
/// </summary>
9+
public sealed class AmbiantLightReading
10+
{
11+
public AmbiantLightReading(
12+
float illuminanceInLux,
13+
TimeSpan? performanceCount,
14+
IReadOnlyDictionary<string, object>? properties,
15+
DateTimeOffset timestamp
16+
)
17+
{
18+
IlluminanceInLux = illuminanceInLux;
19+
PerformanceCount = performanceCount;
20+
Properties = properties;
21+
Timestamp = timestamp;
22+
}
23+
24+
/// <summary>
25+
/// Gets the illuminance level in lux.
26+
/// </summary>
27+
public float IlluminanceInLux { get; }
28+
29+
/// <summary>
30+
/// Gets the performance count associated with the reading.This allows the reading to be synchronized with other devices and processes on the system.
31+
/// </summary>
32+
public TimeSpan? PerformanceCount { get; }
33+
34+
/// <summary>
35+
/// Gets the data properties reported by the sensor.
36+
/// </summary>
37+
public IReadOnlyDictionary<string, object>? Properties { get; }
38+
39+
/// <summary>
40+
/// Gets the time at which the sensor reported the reading.
41+
/// </summary>
42+
public DateTimeOffset Timestamp { get; }
43+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Reactive.Linq;
3+
4+
namespace DeviceCore;
5+
6+
/// <summary>
7+
/// The fake implementation of <see cref="IAmbientLightProvider"/> for testing purposes.
8+
/// </summary>
9+
public sealed class FakeAmbientLightProvider : IAmbientLightProvider
10+
{
11+
/// <inheritdoc/>
12+
public IObservable<AmbiantLightReading?> ObserveCurrentReading()
13+
{
14+
return Observable.Return(new AmbiantLightReading(0f, null, null, DateTimeOffset.Now));
15+
}
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
3+
namespace DeviceCore;
4+
5+
/// <summary>
6+
/// Provides access to the current ambient light (illuminance level) reading in lux and detects changes.
7+
/// </summary>
8+
/// <remarks>
9+
/// The device or emulator that you're using must support an ambient light sensor.
10+
/// </remarks>
11+
public interface IAmbientLightProvider
12+
{
13+
/// <summary>
14+
/// Observes the current ambient light reading.
15+
/// </summary>
16+
/// <remarks>
17+
/// If the device does not support an ambient light sensor, this method will return an observable sequence that yields null.
18+
/// </remarks>
19+
/// <returns>An observable sequence yielding the current ambient light reading.</returns>
20+
IObservable<AmbiantLightReading?> ObserveCurrentReading();
21+
}

0 commit comments

Comments
 (0)