Skip to content
Open
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
67 changes: 67 additions & 0 deletions DeviceCore.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36301.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{AD02B423-808C-484C-B74C-8BD7D1A4A6F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCore.Abstractions", "src\lib\DeviceCore.Abstractions\DeviceCore.Abstractions.csproj", "{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCore.Uno.WinUI", "src\lib\DeviceCore.Uno.WinUI\DeviceCore.Uno.WinUI.csproj", "{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{FA423D9C-B957-4517-9D09-0BF8E8991008}"
ProjectSection(SolutionItems) = preProject
build\azure-pipelines.yml = build\azure-pipelines.yml
build\gitversion.yml = build\gitversion.yml
build\stage-build.yml = build\stage-build.yml
build\stage-release.yml = build\stage-release.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{FD0DD8AF-C1A2-4FB4-9FE3-D039FA20D310}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
.mergify.yml = .mergify.yml
BREAKING_CHANGES.md = BREAKING_CHANGES.md
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
CONTRIBUTING.md = CONTRIBUTING.md
LICENSE = LICENSE
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{DCAB9D57-69DC-4C5D-8703-43C374CB0387}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceCore.Fakes", "src\lib\DeviceCore.Fakes\DeviceCore.Fakes.csproj", "{F50ABEC6-6C96-421D-BF40-67B74CBBB90E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD}.Release|Any CPU.Build.0 = Release|Any CPU
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447}.Release|Any CPU.Build.0 = Release|Any CPU
{F50ABEC6-6C96-421D-BF40-67B74CBBB90E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F50ABEC6-6C96-421D-BF40-67B74CBBB90E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F50ABEC6-6C96-421D-BF40-67B74CBBB90E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F50ABEC6-6C96-421D-BF40-67B74CBBB90E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{26E9BF70-760F-DFCB-CBD5-E521C59D98FD} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{D1516BF4-7E73-CC38-0C50-ABF8C0B4F447} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{F50ABEC6-6C96-421D-BF40-67B74CBBB90E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {483215CF-95DE-4CCF-A682-832BB70A63F4}
EndGlobalSection
EndGlobal
119 changes: 98 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,116 @@
# Open Source Project Template
# Device Core

This repository contains a template to seed a repository for an Open Source
project.
A cross-platform device services library for .NET 8+ and Uno Platform.

## How to use this template
Device Core provides a unified API abstraction to access device hardware features such as accelerometer, ambient light sensor, battery information, flashlight, and screen wake lock across Windows, Android, and iOS.

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

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

The following is the template for the final README.md file:
1. Add the `DeviceCore.Uno.WinUI` NuGet package to your projects (Windows, Android and iOS).
> 💡 If you need to implement more platforms or create custom implementations, you can use the `DeviceCore.Abstractions` NuGet package.

---
1. Create an instance of any service. We'll cover dependency injection in details later on in this documentation.

# Project Title
```cs
using DeviceCore;

{Project tag line}
var flashlightService = new FlashlightService();
```

{Small description of the purpose of the project}
1. Use the service.

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

## Getting Started
## Next Step

### Using Dependency Injection

{Instructions to quickly get started using the project: pre-requisites, packages
to install, sample code, etc.}
Here is a simple code that does dependency injection using `Microsoft.Extensions.DependencyInjection` and `Microsoft.Extensions.Hosting`.

```cs
using DeviceCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
.ConfigureServices(serviceCollection => serviceCollection
.AddSingleton(_ => DispatcherQueue.GetForCurrentThread())
.AddSingleton<IAccelerometerService, AccelerometerService>()
.AddSingleton<IAmbientLightProvider, AmbientLightProvider>()
.AddSingleton<IBatteryInformationProvider, BatteryInformationProvider>()
.AddSingleton<IFlashlightService, FlashlightService>()
.AddSingleton<IScreenWakeLockService, ScreenWakeLockService>()
)
.Build();
```

## Features

{More details/listing of features of the project}
Now that everything is setup, Let's see what else we can do!

### Accelerometer

The `AccelerometerService` provides access to the device's accelerometer sensor, allowing you to read acceleration data in three dimensions (X, Y, Z).

> 💡 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.

> 💡 Unsubscribe from the `ObserveAcceleration` and `ObserveDeviceShaken` observables when you no longer need the readings to avoid unnecessary battery consumption.

> 💡 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.

> 💡 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.

### Light Sensor

The `AmbientLightProvider` provides access to the device's ambient light sensor, allowing you to read the current light level.

> 💡 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.

> 💡 Unsubscribe from the `ObserveCurrentReading` observable when you no longer need the readings to avoid unnecessary battery consumption.

### Battery Information

The `BatteryInformationProvider` provides access to the device's battery information, allowing you to read the current battery level and status.

> 💡 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.

#### Android

To use the battery information on Android, ensure you have the correct permissions in your `AndroidManifest.xml`.

```xml
<uses-permission android:name="android.permission.BATTERY_STATS" />
```

### Flashlight

The `FlashlightService` allows you to turn the phone's camera flashlight on and off.

> 💡 On Android, flashlight brightness cannot be controlled, hence any non-zero brightness level results in the full brightness of the flashlight.

> 💡 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.

#### Android

To use the flashlight on Android, ensure you have the correct permissions in your `AndroidManifest.xml`.

```xml
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.CAMERA" />
```

### Keeping Screen On

To enables an application to request to keep the device's screen on, use the `ScreenWakeLockService`.

## Acknowledgements

Take a look at [Uno.WinRT](https://platform.uno/docs/articles/features/using-winrt.html) that we use for the mobile platforms implementation.

## Breaking Changes

Expand Down
64 changes: 64 additions & 0 deletions build/azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
trigger:
branches:
include:
- main

variables:
# Pipeline configuration (Disable shallow fetch).
# See https://dev.to/kkazala/azure-devops-pipelines-shallow-fetch-1-is-now-default-4656 for more details.
# See https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/pipeline-options-for-git?view=azure-devops&tabs=yaml#shallow-fetch for more details.
- name: Agent.Source.Git.ShallowFetchDepth
value: 0

- name: NUGET_VERSION
value: 6.14.0
- name: windowsHostedAgentImage
value: 'windows-2025'
- name: ArtifactName
value: Packages
- name: SolutionFileName
value: DeviceCore.sln
- name: IsReleaseBranch # Should this branch name use the release stage.
value: $[or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/feature/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))]

stages:
- stage: Build
jobs:
- job: Windows
strategy:
maxParallel: 3
matrix:
Packages:
ApplicationConfiguration: Release
ApplicationPlatform: Any CPU
GeneratePackageOnBuild: true

pool:
vmImage: $(windowsHostedAgentImage)

variables:
- name: PackageOutputPath # Path where NuGet packages will be copied to.
value: $(Build.ArtifactStagingDirectory)

workspace:
clean: all # Cleanup the workspace before starting.

steps:
- checkout: self
fetchDepth: 0

- template: stage-build.yml

- stage: Release
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['IsReleaseBranch'], 'true')) # Only release when the build is not for a Pull Request and branch name fits.
jobs:
- job: Publish_NuGet_External

pool:
vmImage: $(windowsHostedAgentImage)

workspace:
clean: all # Cleanup the workspace before starting.

steps:
- template: stage-release.yml
27 changes: 27 additions & 0 deletions build/gitversion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# The version is driven by conventional commits via xxx-version-bump-message.
# Anything merged to main creates a new stable version.
# Only builds from main and feature/* are pushed to nuget.org.

assembly-versioning-scheme: MajorMinorPatch
mode: MainLine
next-version: '' # Use git tags to set the base version.
continuous-delivery-fallback-tag: ""
commit-message-incrementing: Enabled
major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)"
minor-version-bump-message: "^(feat)(\\([\\w\\s-]*\\))?:"
patch-version-bump-message: "^(build|chore|docs|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?:"
no-bump-message: "^(ci)(\\([\\w\\s-]*\\))?:" # You can use the "ci" type to avoid bumping the version when your changes are limited to the build or .github folders.
branches:
main:
regex: ^master$|^main$
tag: ''
dev:
regex: dev/.*?/(.*?)
tag: dev.{BranchName}
source-branches: [main]
feature:
tag: feature.{BranchName}
regex: feature/(.*?)
source-branches: [main]
ignore:
sha: []
78 changes: 78 additions & 0 deletions build/stage-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
parameters:
DotNetVersion: '8.0.401'
UnoCheck_Version: '1.30.1'
UnoCheck_Manifest: 'https://raw.githubusercontent.com/unoplatform/uno.check/0ca039bef4097295fc6c2c5c282ae18a797160c1/manifests/uno.ui.manifest.json'

steps:
- task: GitVersion/setup@0
inputs:
versionSpec: '5.10.1'
displayName: 'Install GitVersion'

- task: GitVersion/execute@0
inputs:
useConfigFile: true
configFilePath: $(Build.SourcesDirectory)/build/gitversion.yml
displayName: 'Calculate version'

- task: UseDotNet@2
displayName: 'Use .NET SDK ${{ parameters.DotNetVersion }}'
retryCountOnTaskFailure: 3
inputs:
packageType: sdk
version: ${{ parameters.DotNetVersion }}
includePreviewVersions: true

- powershell: |
& dotnet tool update --global uno.check --version ${{ parameters.UnoCheck_Version }} --add-source https://api.nuget.org/v3/index.json
& uno-check -v --ci --non-interactive --fix --skip xcode --skip gtk3 --skip vswin --skip vsmac --skip androidsdk --skip androidemulator --manifest ${{ parameters.UnoCheck_Manifest }}
displayName: Install .NET Workloads | Uno-check
errorActionPreference: continue
ignoreLASTEXITCODE: true
retryCountOnTaskFailure: 3

- task: MSBuild@1
displayName: 'Restore Solution Packages'
inputs:
solution: $(Build.SourcesDirectory)/$(SolutionFileName)
msbuildLocationMethod: version
msbuildVersion: latest
msbuildArchitecture: x64
msbuildArguments: >
/t:restore
configuration: $(ApplicationConfiguration)
platform: $(ApplicationPlatform)
clean: false
maximumCpuCount: true
restoreNugetPackages: false
logProjectEvents: false
createLogFile: false

- task: MSBuild@1
displayName: 'Build solution in $(ApplicationConfiguration) | $(ApplicationPlatform)'
inputs:
solution: $(Build.SourcesDirectory)/$(SolutionFileName)
msbuildLocationMethod: version
msbuildVersion: latest
msbuildArchitecture: x64
configuration: $(ApplicationConfiguration)
platform: $(ApplicationPlatform)
clean: false
maximumCpuCount: true
restoreNugetPackages: false
logProjectEvents: false
createLogFile: false
msbuildArguments: > # Set the version of the packages, will have no effect on application projects (Heads).
/p:PackageVersion=$(GitVersion.SemVer)
/p:ContinuousIntegrationBuild=true

- task: PublishBuildArtifacts@1
displayName: 'Publish artifact $(ApplicationConfiguration)'
inputs:
PathtoPublish: $(PackageOutputPath)
ArtifactName: $(ArtifactName)
ArtifactType: Container

- task: PostBuildCleanup@3
displayName: 'Post-Build cleanup : Cleanup files to keep build server clean!'
condition: always()
Loading