* Initial articles

* Update include format/path

* First two articles ported

* Feedback

* Fix includes

* Remove xamarin vid include & vid sections

* Add new include 4  essentials namespace

* Article updates

* Feedback

* Two docs; del color convert, n/a

* Mass find/replace on common problems

* Replace yes/no/warn with emoji

* Port sensor related essentials to sensor.md

* Updates to latex;intro

* add description

* Fix copy\paste errors+more

* more feedback

* Fix TOC

* Move essentials

* Another set of migrated

* Feedback from previous PR

* Process more articles

* handle feedback

* More items for review

* feedback

* Lots of updates

* More fixes

* Feedback

* Another set

* temp

* temp

* 99% finished

* final article

* Handled feedback

* Remove troubleshooting article

* SemanticScreenReader (#177)

* essentials restructure 1

* Ported sensors and added code

* minor adjustments

* add namespace note; under construction note

* fix sensors

* link fixes

* fix essentialsindex

* fix links to includes that will go away

* move images

* more include bugs

* move includes to shared area

* fix last errors hopefully!

* add battery

* Minor

* more code

* Add more device features

* fix toc

* Update code

* updated more articles

* toc

* fix code snippet

* fix code snippet

* Add app info

* more ported articles

* remove ref to source code

* Comms

* reader

* fixes

* moar!!!!

* Toc; file rename

* web auth

* storage area

* networking

* lint

* remove old namespace

* build fixes

* fix the snippet links

* in progress 1

* in progress 2

* in progress 3

* in progress 4

* Update app-startup to talk about essentials

* in progress 5

* more app startup updates

* handle build errors

* fix link

* Feedback fixes

* secure storage update

* try fix build errs

* restore file

* redirects

* Version tracking > geocoding.

* Android differences update.

* Update dates.

* Comment out UWP API doc links for now.

* Revert "redirects"

This reverts commit fe02dce4cb.

* Restore supported platforms.

* Rearrange TOC.

* Edits.

Co-authored-by: Andy De George <thraka.junk@outlook.com>
Co-authored-by: David Britch <davidbritch@users.noreply.github.com>
This commit is contained in:
Andy (Steve) De George 2022-05-23 03:14:01 -07:00 коммит произвёл GitHub
Родитель 0750b25695
Коммит 537d032d98
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
191 изменённых файлов: 11218 добавлений и 5 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -1,9 +1,11 @@
log/
bin/
obj/
_site/
.optemp/
_themes*/
_repo.*/
.vs/
.openpublishing.buildcore.ps1
.DS_Store

Просмотреть файл

@ -413,6 +413,84 @@
href: user-interface/visual-states.md
- name: Platform integration
items:
- name: Application model
items:
- name: App actions
href: platform-integration/appmodel/app-actions.md
- name: App information
href: platform-integration/appmodel/app-information.md
- name: Browser
href: platform-integration/appmodel/open-browser.md
- name: Launcher
href: platform-integration/appmodel/launcher.md
- name: Main thread
href: platform-integration/appmodel/main-thread.md
- name: Maps
href: platform-integration/appmodel/maps.md
- name: Permissions
href: platform-integration/appmodel/permissions.md
- name: Version tracking
href: platform-integration/appmodel/version-tracking.md
- name: Communication
items:
- name: Contacts
href: platform-integration/communication/contacts.md
- name: Email
href: platform-integration/communication/email.md
- name: Networking
href: platform-integration/communication/networking.md
- name: Phone dialer
href: platform-integration/communication/phone-dialer.md
- name: SMS (messaging)
href: platform-integration/communication/sms.md
- name: Web authentication
href: platform-integration/communication/authentication.md
- name: Device features
items:
- name: Battery
href: platform-integration/device/battery.md
- name: Device display
href: platform-integration/device/display.md
- name: Device information
href: platform-integration/device/information.md
- name: Device sensors
href: platform-integration/device/sensors.md
- name: Flashlight
href: platform-integration/device/flashlight.md
- name: Geocoding
href: platform-integration/device/geocoding.md
- name: Geolocation
href: platform-integration/device/geolocation.md
- name: Haptic feedback
href: platform-integration/device/haptic-feedback.md
- name: Vibration
href: platform-integration/device/vibrate.md
- name: Media
items:
- name: Photo picker
href: platform-integration/device-media/picker.md
- name: Screenshot
href: platform-integration/device-media/screenshot.md
- name: Text-to-speech
href: platform-integration/device-media/text-to-speech.md
- name: Unit converters
href: platform-integration/device-media/unit-converters.md
- name: Sharing
items:
- name: Clipboard
href: platform-integration/data/clipboard.md
- name: Share files and text
href: platform-integration/data/share.md
- name: Storage
items:
- name: File picker
href: platform-integration/storage/file-picker.md
- name: File system helpers
href: platform-integration/storage/file-system-helpers.md
- name: Preferences
href: platform-integration/storage/preferences.md
- name: Secure storage
href: platform-integration/storage/secure-storage.md
- name: Configure multi-targeting
href: platform-integration/configure-multi-targeting.md
- name: Invoke platform code

Просмотреть файл

@ -176,13 +176,17 @@ label.SetSemanticFocus();
## Semantic screen reader
.NET MAUI Essentials includes a `SemanticScreenReader` class that enables you to instruct a screen reader to announce specified text. This can be achieved by calling the `SemanticScreenReader.Announce` method, passing a `string` argument that represents the text:
.NET Maui provides the `ISemanticScreenReader` interface, with which you can instruct a screen reader to announce text to the user. The interface is exposed through the `SemanticScreenReader.Default` property, and is available in the `Microsoft.Maui.Accessability` namespace.
To instruct a screen reader to announce text, use the `Announce` method, passing a `string` argument that represents the text. The following example demonstrates using this method:
```csharp
SemanticScreenReader.Announce("This is the announcement text.");
SemanticScreenReader.Default.Announce("This is the announcement text.");
```
<!-- For more information, see [SemanticScreenReader]().-->
### Limitations
The default platform screen reader must be enabled for text to be read aloud.
<!--
### Semantic effects

Просмотреть файл

@ -16,8 +16,6 @@ landingContent:
links:
- text: What is .NET Multi-platform App UI?
url: what-is-maui.md
- text: Supported platforms
url: supported-platforms.md
- title: Get started
linkLists:
- linkListType: how-to-guide

Просмотреть файл

@ -0,0 +1,88 @@
---
title: "App actions (shortcuts)"
description: "Describes the IAppActions interface in the Microsoft.Maui.ApplicationModel namespace, which lets you create and respond to app shortcuts from the app icon."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel", "AppDelegate.cs", "AppActions", "Platforms/Android/MainActivity.cs", "Platforms/iOS/AppDelegate.cs", "Platforms/Windows/App.xaml.cs", "Id", "Title", "Subtitle", "Icon"]
---
# App actions
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IAppActions` interface, which lets you create and respond to app shortcuts. App shortcuts are helpful to users because they allow you, as the app developer, to present them with extra ways of starting your app. For example, if you were developing an email and calendar app, you could present two different app actions, one to open the app directly to the current day of the calendar, and another to open to the email inbox folder.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `IAppActions` interface is exposed through the `AppActions.Current` property, and is available in the `Microsoft.Maui.ApplicationModel` namespace.
## Get started
To access the `AppActions` functionality, the following platform specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
In the _Platforms/Android/MainActivity.cs_ file, add the following `IntentFilter` attribute to the `MainActivity` class:
:::code language="csharp" source="../snippets/shared_2/Platforms/Android/MainActivity.cs" id="intent_filter_1":::
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Create actions
App actions can be created at any time, but are often created when an app starts. To configure app actions, add the `ConfigureEssentials` step to the `CreateMauiApp` bootstrap code. The app startup code is configured in the _MauiProgram.cs_ file. There are two methods you must call to enable an app action:
01. `AddAppAction`
This method creates an action. It takes an `id` string to uniquely identify the action, and a `title` string that's displayed to the user. You can optionally provide a subtitle and an icon.
01. `OnAppAction`
The delegate passed to this method is called when the user invokes an app action, provided the app action instance. Check the `Id` property of the action to determine which app action was started by the user.
The following code demonstrates how to configure the app actions at app startup:
:::code language="csharp" source="../snippets/shared_1/MauiProgram.cs" id="bootstrap_appaction" highlight="12-18":::
## Responding to actions
After app actions [have been configured](#create-actions), the `OnAppAction` method is called for all app actions invoked by the user. Use the `Id` property to differentiate them. The following code demonstrates handling an app action:
:::code language="csharp" source="../snippets/shared_1/App.xaml.cs" id="appaction_handle":::
### Check if app actions are supported
When you create an app action, either at app startup or while the app is being used, check to see if app actions are supported by reading the `AppActions.Current.IsSupported` property.
### Create an app action outside of the startup bootstrap
To create app actions, call the `SetAsync` method:
:::code language="csharp" source="../snippets/shared_2/MainPage.xaml.cs" id="app_actions":::
### More information about app actions
If app actions aren't supported on the specific version of the operating system, a `FeatureNotSupportedException` will be thrown.
The following properties can be set on an `AppAction`:
- **Id**: A unique identifier used to respond to the action tap.
- **Title**: the visible title to display.
- **Subtitle**: If supported a subtitle to display under the title.
- **Icon**: Must match icons in the corresponding resources directory on each platform.
<!-- TODO icon in image needs update -->
:::image type="content" source="media/app-actions/appactions.png" alt-text="App actions on home screen.":::
## Get actions
You can get the current list of app actions by calling `AppActions.Current.GetAsync()`.

Просмотреть файл

@ -0,0 +1,104 @@
---
title: "App Information"
description: "Describes the IAppInfo interface in the Microsoft.Maui.ApplicationModel namespace, which provides information about your application. For example, it exposes the app name and version."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# App information
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IAppInfo` interface, which provides information about your application. The `IAppInfo` interface is exposed through the `AppInfo.Current` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `AppInfo` and `IAppInfo` types are available in the `Microsoft.Maui.ApplicationModel` namespace.
## Read the app information
There are four properties exposed by the `IAppInfo` interface:
- `IAppInfo.Name` &mdash; The application name
- `IAppInfo.PackageName` &mdash; The package name or application identifier, such as `com.microsoft.myapp`.
- `IAppInfo.VersionString` &mdash; The application version, such as `1.0.0`.
- `IAppInfo.BuildString` &mdash; The build number of the version, such as `1000`.
The following code example demonstrates accessing these properties:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="read_info":::
## Read the current theme
The `RequestedTheme` property provides the current requested theme by the system for your application. One of the following values is returned:
- `Unspecified`
- `Light`
- `Dark`
`Unspecified` is returned when the operating system doesn't have a specific user interface style. An example of this is on devices running versions of iOS older than 13.0.
The following code example demonstrates reading the theme:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="read_theme":::
## Display app settings
The `IAppInfo` class can also display a page of settings maintained by the operating system for the application:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="show_settings":::
This settings page allows the user to change application permissions and perform other platform-specific tasks.
## Platform implementation specifics
This section describes platform-specific implementation details related to the `IAppInfo` interface.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
App information is taken from the _AndroidManifest.xml_ for the following fields:
- **Build** &mdash; `android:versionCode` in `manifest` node
- **Name** &mdash; `android:label` in the `application` node
- **PackageName** &mdash; `package` in the `manifest` node
- **VersionString** &mdash; `android:versionName` in the `application` node
### Requested theme
Android uses configuration modes to specify the type of theme to request from the user. Based on the version of Android, it can be changed by the user or may be changed when battery saver mode is enabled.
You can read more on the official [Android documentation for Dark Theme](https://developer.android.com/guide/topics/ui/look-and-feel/darktheme).
# [iOS](#tab/ios)
App information is taken from the _Info.plist_ for the following fields:
- **Build** &mdash; `CFBundleVersion`
- **Name** &mdash; `CFBundleDisplayName` if set, else `CFBundleName`
- **PackageName** &mdash; `CFBundleIdentifier`
- **VersionString** &mdash; `CFBundleShortVersionString`
### Requested theme
_Unspecified_ is always returned on versions of iOS older than 13.0
# [Windows](#tab/windows)
App information is taken from the _Package.appxmanifest_ for the following fields:
- **Build** &mdash; Uses the `Build` from the `Version` on the `Identity` node
- **Name** &mdash; `DisplayName` on the `Properties` node
- **PackageName** &mdash; `Name` on the `Identity` node
- **VersionString** &mdash; `Version` on the `Identity` node
### Requested theme
Code that accesses the `IAppInfo.RequestedTheme` property must be called on the UI thread or an exception will be thrown.
Windows applications respect the `RequestedTheme` property setting in the Windows _App.xaml_. If it's set to a specific theme, this API always returns this setting. To use the dynamic theme of the OS, remove this property from your application. When your app is run, it returns the theme set by the user in Windows settings: **Settings** > **Personalization** > **Colors** > **Choose your default app mode**.
<!-- TODO: You can read more on the [Windows Requested Theme Documentation](/uwp/api/windows.ui.xaml.application.requestedtheme). -->
--------------
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,95 @@
---
title: "Launcher"
description: "Learn how to use the .NET MAUI ILauncher interface in the Microsoft.Maui.ApplicationModel namespace, which can open another application by URI."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# Launcher
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `ILauncher` interface. This interface enables an application to open a URI by the system. This is often used when deep linking into another application's custom URI schemes. The `ILauncher` interface is exposed through the `Launcher.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
> [!IMPORTANT]
> To open the browser to a website, use the [Browser](open-browser.md) API instead.
## Get started
To access the launcher functionality, the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
No setup is required.
# [iOS](#tab/ios)
Apple requires that you define the schemes you want to use. Add the `LSApplicationQueriesSchemes` key and schemes to the _Platforms/iOS/Info.plist_ file:
```xml
<key>LSApplicationQueriesSchemes</key>
<array>
<string>lyft</string>
<string>fb</string>
</array>
```
The `<string>` elements are the URI schemes preregistered with your app. You can't use schemes outside of this list.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Open another app
To use the Launcher functionality, call the `Launcher.OpenAsync` method and pass in a `string` or `Uri` representing the app to open. Optionally, the `Launcher.CanOpenAsync` method can be used to check if the URI scheme can be handled by an app on the device. The following code demonstrates how to check if a URI scheme is supported or not, and then opens the URI:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="launcher_open":::
The previous code example can be simplified by using the `TryOpenAsync`, which checks if the URI scheme can be opened, before opening it:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="launcher_open_try":::
## Open another app via a file
The launcher can also be used to open an app with a selected file. .NET MAUI automatically detects the file type (MIME), and opens the default app for that file type. If more than one app is registered with the file type, an app selection popover is shown to the user.
The following code example writes text to a file, and opens the text file with the launcher:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="launcher_open_file":::
## Set the launcher location
[!INCLUDE [ios-PresentationSourceBounds](../includes/ios-PresentationSourceBounds.md)]
## Platform differences
This section describes the platform-specific differences with the launcher API.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
# [Android](#tab/android)
The `Task` returned from `CanOpenAsync` completes immediately.
# [iOS](#tab/ios)
The `Task` returned from `CanOpenAsync` completes immediately.
If the target app on the device has never been opened by your application with `OpenAsync`, iOS displays a popover to the user, requesting permission to allow this action.
<!-- TODO: where does this go?
For more information about the iOS implementation, see [TITLE](xref:UIKit.UIApplication.CanOpenUrl*)
-->
# [Windows](#tab/windows)
No platform differences.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,50 @@
---
title: Run code on the main UI thread
description: "In .NET MAUI, event handlers may be called on a secondary thread. The MainThread class allows an application to run code on the main UI thread. This article describes how to use the MainThread class."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# Create a thread on the .NET MAUI UI thread
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `MainThread` class to run code on the main UI thread. Most operating systems use a single-threading model for code involving the user interface. This model is necessary to properly serialize user-interface events, including keystrokes and touch input. This thread is often called the _main thread_, the _user-interface thread_, or the _UI thread_. The disadvantage of this model is that all code that accesses user interface elements must run on the application's main thread.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `MainThread` class is available in the `Microsoft.Maui.ApplicationModel` namespace.
## When is it required
Applications sometimes need to use events that call the event handler on a secondary thread, such as the [`Accelerometer`](../device/sensors.md#accelerometer) or [`Compass`](../device/sensors.md#compass) sensors. All sensors might return information on a secondary thread when used with faster sensing speeds. If the event handler needs to access user-interface elements, it must invoke code on the main thread.
## Run code on the UI thread
To run code on the main thread, call the static `MainThread.BeginInvokeOnMainThread` method. The argument is an [`Action`](xref:System.Action) object, which is simply a method with no arguments and no return value:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="runcode_lambda":::
It is also possible to define a separate method for the code, and then call that code with the `BeginInvokeOnMainThread` method:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="runcode_func_pointer":::
## Determine if invocation is required
With the `MainThread` class, you can determine if the current code is running on the main thread. The `MainThread.IsMainThread` property returns `true` if the code calling the property is running on the main thread, and `false` if it isn't. It's logical to assume that you need to determine if the code is running on the main thread before calling `BeginInvokeOnMainThread`. For example, the following code uses the `MainThread.IsMainThread` to detect if the `MyMainThreadCode` method should be called directly if the code is running on the main thread. If it isn't running on the main thread, the method is passed to `MainThread.BeginInvokeOnMainThread`:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="runcode_test_thread":::
This check isn't necessary. `BeginInvokeOnMainThread` itself tests if the current code is running on the main thread or not. If the code is running on the main thread, `BeginInvokeOnMainThread` just calls the provided method directly. If the code is running on a secondary thread, `BeginInvokeOnMainThread` invokes the provided method on the main thread. Therefore, if the code you run is the same, regardless of the main or secondary thread, simply call `BeginInvokeOnMainThread` without checking if it's required. There is negligible overhead in doing so.
The only reason you would need to check the `MainThread.IsMainThread` property is if you have branching logic that does something different based on the thread.
## Additional methods
The `MainThread` class includes the following additional `static` methods that can be used to interact with user interface elements from backgrounds threads:
| Method | Arguments | Returns | Purpose |
|------------------------------|-----------------|-----------|-----------------------------------------------------------------------------|
| `InvokeOnMainThreadAsync<T>` | `Func<T>` | `Task<T>` | Invokes a `Func<T>` on the main thread, and waits for it to complete. |
| `InvokeOnMainThreadAsync` | `Action` | `Task` | Invokes an `Action` on the main thread, and waits for it to complete. |
| `InvokeOnMainThreadAsync<T>` | `Func<Task<T>>` | `Task<T>` | Invokes a `Func<Task<T>>` on the main thread, and waits for it to complete. |
| `InvokeOnMainThreadAsync` | `Func<Task>` | `Task` | Invokes a `Func<Task>` on the main thread, and waits for it to complete. |
| `GetMainThreadSynchronizationContextAsync` | | `Task<SynchronizationContext>` | Returns the `SynchronizationContext` for the main thread. |

Просмотреть файл

@ -0,0 +1,66 @@
---
title: "Map"
description: "Learn how to use the .NET MAUI Map class in the Microsoft.Maui.ApplicationModel namespace. This class enables an application to open the installed map application to a specific location or placemark."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# Map
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IMap` interface. This interface enables an application to open the installed map application to a specific location or placemark. A default implementation of the `IMap` interface is exposed through the `Map.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `IMap` and `Map` types are available in the `Microsoft.Maui.ApplicationModel` namespace.
## Using the map
The map functionality works by calling the `Map.OpenAsync` method, and passing either an instance of the `Location` or `Placemark` type. The following example opens the installed map app at a specific GPS location:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="navigate_building":::
> [!TIP]
> The `Location` and `Placemark` types are in the `Microsoft.Maui.Devices.Sensors` namespace.
When you use a `Placemark` to open the map, more information is required. The information helps the map app search for the place you're looking for. The following information is required:
- `CountryName`
- `AdminArea`
- `Thoroughfare`
- `Locality`
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="navigate_building_placemark":::
## Extension methods
As long as the `Microsoft.Maui.Devices.Sensors` namespace is imported, which a new .NET MAUI project automatically does, you can use the built-in extension method `OpenMapAsync` to open the map:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="navigate_building_placemark_extension":::
## Add navigation
When you open the map, you can calculate a route from the device's current location to the specified location. Pass the `MapLaunchOptions` type to the `Map.OpenAsync` method, specifying the navigation mode. The following example opens the map app and specifies a driving navigation mode:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="navigate_building_driving":::
## Platform differences
This section describes the platform-specific differences with the maps API.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
`NavigationMode` supports Bicycling, Driving, and Walking.
Android uses the `geo:` Uri scheme to launch the maps application on the device. This may prompt the user to select from an existing app that supports this Uri scheme. Google Maps supports this scheme.
# [iOS](#tab/ios)
`NavigationMode` supports Driving, Transit, and Walking.
# [Windows](#tab/windows)
`NavigationMode` supports Driving, Transit, and Walking.
-----
<!-- markdownlint-enable MD025 -->

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 184 KiB

Просмотреть файл

@ -0,0 +1,101 @@
---
title: "Open the browser"
description: "The Browser class in the Microsoft.Maui.ApplicationModel namespace enables an application to open a web link in the optimized system preferred browser or the external browser."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# Browser
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IBrowser` interface. This interface enables an application to open a web link in the system-preferred browser or the external browser. A default implementation of the `IBrowser` interface is exposed through the `Browser.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
## Get started
To access the browser functionality, the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
If your project's Target Android version is set to **Android 11 (R API 30)** or higher, you must update your _Android Manifest_ with queries that use Android's [package visibility requirements](https://developer.android.com/preview/privacy/package-visibility).
In the _Platforms/Android/AndroidManifest.xml_ file, add the following `queries/intent` nodes the `manifest` node:
```xml
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https"/>
</intent>
</queries>
```
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Open the browser
The browser is opened by calling the `Browser.OpenAsync` method with the `Uri` and the type of `BrowserLaunchMode`. The following code example demonstrates opening the browser:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="browser_open":::
This method returns after the browser is launched, not after it was closed by the user. `Browser.OpenAsync` returns a `bool` value to indicate if the browser was successfully launched.
## Customization
When using the system preferred browser, there are several customization options available for iOS and Android. This includes a `TitleMode` (Android only), and preferred color options for the `Toolbar` (iOS and Android) and `Controls` (iOS only) that appear.
These options are specified using `BrowserLaunchOptions` when calling `OpenAsync`.
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="browser_open_custom":::
## Platform differences
This section describes the platform-specific differences with the browser API.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
### [Android](#tab/android)
The `BrowserLaunchOptions.LaunchMode` determines how the browser is launched:
- `SystemPreferred`
[Custom Tabs](https://developer.chrome.com/multidevice/android/customtabs) will try to be used to load the Uri and keep navigation awareness.
- `External`
An `Intent` is used to request the Uri be opened through the system's normal browser.
# [iOS](#tab/ios)
The `BrowserLaunchOptions.LaunchMode` determines how the browser is launched:
- `SystemPreferred`
[SFSafariViewController](xref:SafariServices.SFSafariViewController) is used to load the Uri and keep navigation awareness.
- `External`
The standard `OpenUrl` on the main application is used to launch the default browser outside of the application.
# [Windows](#tab/windows)
The user's default browser will always be launched regardless of the `BrowserLaunchMode`.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,181 @@
---
title: "Permissions"
description: "Learn how to use the .NET MAUI Permissions class, to check and request permissions. This class is in the Microsoft.Maui.ApplicationModel namespace."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# Permissions
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `Permissions` class. This class allows you to check and request permissions at run-time. The `Permissions` type is available in the `Microsoft.Maui.ApplicationModel` namespace.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
## Available permissions
.NET MAUI attempts to abstract as many permissions as possible. However, each operating system has a different set of permissions. Even though the API allows access to a common permission, there may be differences between operating systems related to that permission. The following table describes the available permissions:
The following table uses ✔️ to indicate that the permission is supported and ❌ to indicate the permission isn't supported or isn't required:
| Permission | Android | iOS | Windows | tvOS |
|-------------------|:-------:|:---:|:-------:|:----:|
| CalendarRead | ✔️ | ✔️ | ❌ | ❌ |
| CalendarWrite | ✔️ | ✔️ | ❌ | ❌ |
| Camera | ✔️ | ✔️ | ❌ | ❌ |
| ContactsRead | ✔️ | ✔️ | ✔️ | ❌ |
| ContactsWrite | ✔️ | ✔️ | ✔️ | ❌ |
| Flashlight | ✔️ | ❌ | ❌ | ❌ |
| LocationWhenInUse | ✔️ | ✔️ | ✔️ | ✔️ |
| LocationAlways | ✔️ | ✔️ | ✔️ | ❌ |
| Media | ❌ | ✔️ | ❌ | ❌ |
| Microphone | ✔️ | ✔️ | ✔️ | ❌ |
| Phone | ✔️ | ✔️ | ❌ | ❌ |
| Photos | ❌ | ✔️ | ❌ | ✔️ |
| Reminders | ❌ | ✔️ | ❌ | ❌ |
| Sensors | ✔️ | ✔️ | ✔️ | ❌ |
| Sms | ✔️ | ✔️ | ❌ | ❌ |
| Speech | ✔️ | ✔️ | ❌ | ❌ |
| StorageRead | ✔️ | ❌ | ❌ | ❌ |
| StorageWrite | ✔️ | ❌ | ❌ | ❌ |
If a permission is marked as ❌, it will always return `Granted` when checked or requested.
## Checking permissions
To check the current status of a permission, use the `Permissions.CheckStatusAsync` method along with the specific permission to get the status for. The following example checks the status of the `LocationWhenInUse` permission:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="permission_check":::
A `PermissionException` is thrown if the required permission isn't declared.
It's best to check the status of the permission before requesting it. Each operating system returns a different default state, if the user has never been prompted. iOS returns `Unknown`, while others return `Denied`. If the status is `Granted` then there's no need to make other calls. On iOS if the status is `Denied` you should prompt the user to change the permission in the settings. On Android, you can call `ShouldShowRationale` to detect if the user has already denied the permission in the past.
### Permission status
When using `CheckStatusAsync` or `RequestAsync`, a `PermissionStatus` is returned that can be used to determine the next steps:
- `Unknown`\
The permission is in an unknown state, or on iOS, the user has never been prompted.
- `Denied`\
The user denied the permission request.
- `Disabled`\
The feature is disabled on the device.
- `Granted`\
The user granted permission or is automatically granted.
- `Restricted`\
In a restricted state.
## Requesting permissions
To request a permission from the users, use the `Permissions.RequestAsync` method along with the specific permission to request. If the user previously granted permission, and hasn't revoked it, then this method will return `Granted` without showing a dialog to the user. The following example requests the `LocationWhenInUse` permission:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="permission_request":::
A `PermissionException` is thrown if the required permission isn't declared.
> [!IMPORTANT]
> On some platforms, a permission request can only be activated a single time. Further prompts must be handled by the developer to check if a permission is in the `Denied` state, and then ask the user to manually turn it on.
## Explain why permission is needed
It's best practice to explain to your user why your application needs a specific permission. On iOS, you must specify a string that is displayed to the user. Android doesn't have this ability, and also defaults permission status to `Disabled`. This limits the ability to know if the user denied the permission or if it's the first time the permission is being requested. The `ShouldShowRationale` method can be used to determine if an informative UI should be displayed. If the method returns `true`, this is because the user has denied or disabled the permission in the past. Other platforms always return `false` when calling this method.
## Example
The following code presents the general usage pattern for determining whether a permission has been granted, and then requesting it if it hasn't.
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="permission_check_and_request":::
## Extending permissions
The Permissions API was created to be flexible and extensible for applications that require more validation or permissions that aren't included in .NET MAUI. Create a class that inherits from `Permissions.BasePermission`, and implement the required abstract methods. The following example code demonstrates the basic abstract members, but without implementation:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="permission_class":::
When implementing a permission in a specific platform, the `BasePlatformPermission` class can be inherited from. This class provides extra platform helper methods to automatically check the permission declarations. This helps when creating custom permissions that do groupings, for example requesting both **Read** and **Write** access to storage on Android. The following code example demonstrates requesting **Read** and **Write** storage access:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="permission_readwrite":::
You then check the permission in the same way as any other permission type provided by .NET MAUI:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="permission_readwrite_request":::
<!-- Cutting this
It should be updated to demonstrate creating a class in each of the platform project folders, or by simply using #if OS checks
However, at this time VS isn't giving me the correct APIs when I swap from Android to Windows, so I can't even try to write
any code outside of Android
Also, this talks about the "shared" project concept which isn't required in .NET MAUI. I would also assume that
using DependencyService.Register isn't required. So this code will have to be updated too.
>
If you wanted to call this API from your shared code, you could create an interface and use a dependency service to register and get the implementation.
```csharp
public interface IReadWritePermission
{
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
```
Then implement the interface in your platform project:
```csharp
public class ReadWriteStoragePermission : Permissions.BasePlatformPermission, IReadWritePermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)>
{
(Android.Manifest.Permission.ReadExternalStorage, true),
(Android.Manifest.Permission.WriteExternalStorage, true)
}.ToArray();
}
```
You can then register the specific implementation:
```csharp
DependencyService.Register<IReadWritePermission, ReadWriteStoragePermission>();
```
Then from your shared project you can resolve and use it:
```csharp
var readWritePermission = DependencyService.Get<IReadWritePermission>();
var status = await readWritePermission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await readWritePermission.RequestAsync();
}
```
-->
## Platform differences
This section describes the platform-specific differences with the permissions API.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
Permissions must have the matching attributes set in the Android Manifest file. Permission status defaults to `Denied`.
<!-- TODO For more information, see [Permissions in .NET MAUI for Android](../../android/app-fundamentals/permissions.md). -->
# [iOS](#tab/ios)
Permissions must have a matching string in the _Info.plist_ file. Once a permission is requested and denied, a pop-up will no longer appear if you request the permission a second time. You must prompt your user to manually adjust the setting in the applications settings screen in iOS. Permission status defaults to `Unknown`.
<!-- TODO For more information, see [iOS Security and Privacy Features](../ios/app-fundamentals/security-privacy.md). -->
# [Windows](#tab/windows)
Permissions must have matching capabilities declared in the package manifest. Permission status defaults to `Unknown` in most instances.
<!-- TODO For more information, see [App Capability Declaration](/windows/uwp/packaging/app-capability-declarations). -->
-----
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,32 @@
---
title: "Version tracking"
description: "Learn how to use the .NET MAUI VersionTracking class, which lets you check the applications version and build numbers along with seeing additional information."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"]
---
# Version tracking
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IVersionTracking` interface. This class lets you check the applications version and build numbers along with seeing additional information such as if it's the first time the application launched. The `IVersionTracking` interface is exposed through the `VersionTracking.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `VersionTracking` and `IVersionTracking` types are available in the `Microsoft.Maui.ApplicationModel` namespace.
## Get started
To enable version tracking in your app, add the `ConfigureEssentials` step to the `CreateMauiApp` bootstrap code. The app startup code is configured in the _MauiProgram.cs_ file. Call the `UseVersionTracking` method to enable version tracking.
:::code language="csharp" source="../snippets/shared_1/MauiProgram.cs" id="bootstrap_versiontracking" highlight="12-15":::
## Check the version
The `IVersionTracking` interface provides many properties that describe the current version of the app and how it relates to the previous version. The following example writes the tracking information to labels on the page:
:::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="version_read":::
The first time the app is run after version tracking is enabled, the `IsFirstLaunchEver` property will return `true`. If you add version tracking in a newer version of an already released app, `IsFirstLaunchEver` may incorrectly report `true`. This property always returns `true` the first time version tracking is enabled and the user runs the app. You can't fully rely on this property if users have upgraded from older versions that weren't tracking the version.
## Platform differences
All version information is stored using the [Preferences](../storage/preferences.md) API, and is stored with a filename of _[YOUR-APP-PACKAGE-ID].microsoft.maui.essentials.versiontracking_ and follows the same data persistence outlined in the [Preferences](../storage/preferences.md#persistence) documentation.

Просмотреть файл

@ -0,0 +1,308 @@
---
title: "Web Authenticator"
description: "Learn how to use the .NET MAUI WebAuthenticator class, which lets you start browser-based authentication flows, which listen for a callback to the app."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Authentication"]
---
# Web authenticator
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) the `IWebAuthenticator` interface. This interface lets you start browser-based authentication flows, which listen for a callback to a specific URL registered to the app. The `IWebAuthenticator` interface is exposed through the `WebAuthenticator.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `WebAuthenticator` and `IWebAuthenticator` types are available in the `Microsoft.Maui.Authentication` namespace.
## Overview
Many apps require adding user authentication, and this often means enabling your users to sign in to their existing Microsoft, Facebook, Google, or Apple Sign In account.
> [!TIP]
> [Microsoft Authentication Library (MSAL)](/azure/active-directory/develop/msal-overview) provides an excellent turn-key solution to adding authentication to your app.
If you're interested in using your own web service for authentication, it's possible to use `WebAuthenticator` to implement the client-side functionality.
## Why use a server back end
Many authentication providers have moved to only offering explicit or two-legged authentication flows to ensure better security. This means you'll need a **client secret** from the provider to complete the authentication flow. Unfortunately, mobile apps aren't a great place to store secrets and anything stored in a mobile app's code, binaries, or otherwise, is considered to be insecure.
The best practice here's to use a web backend as a middle layer between your mobile app and the authentication provider.
> [!IMPORTANT]
> We strongly recommend against using older mobile-only authentication libraries and patterns which do not leverage a web backend in the authentication flow, due to their inherent lack of security for storing client secrets.
## Get started
To access the `WebAuthenticator` functionality the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
Android requires an **Intent Filter** setup to handle your callback URI. This is accomplished by inheriting from the `WebAuthenticatorCallbackActivity` class:
```csharp
using Android.App;
using Android.Content.PM;
namespace YourRootNamespace
{
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionView },
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
DataScheme = CALLBACK_SCHEME)]
public class WebAuthenticationCallbackActivity : Microsoft.Maui.WebAuthenticatorCallbackActivity
{
const string CALLBACK_SCHEME = "myapp";
}
}
```
If your project's Target Android version is set to **Android 11 (R API 30)** or higher, you must update your _Android Manifest_ with queries that use Android's [package visibility requirements](https://developer.android.com/preview/privacy/package-visibility).
In the _Platforms/Android/AndroidManifest.xml_ file, add the following `queries/intent` nodes the `manifest` node:
```xml
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
```
# [iOS](#tab/ios)
Add your app's callback URI pattern to the _Platforms/iOS/Info.plist_ file:
```xml
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>My App</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
```
# [Windows](#tab/windows)
> [!CAUTION]
> At the moment, `WebAuthenticator` isn't working on Windows. For more information, see [GitHub issue #2702](https://github.com/dotnet/maui/issues/2702).
For WinUI 3, you'll need to declare your callback URI protocol in your _Package.appxmanifest_ file:
```xml
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="MyApp.App">
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="myapp">
<uap:DisplayName>My App</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
</Extensions>
</Application>
</Applications>
```
-----
## Using WebAuthenticator
The API consists mainly of a single method, `AuthenticateAsync`, which takes two parameters:
01. The URL used to start the web browser flow.
01. The URI the flow is expected to ultimately call back to, that is registered to your app.
The result is a `WebAuthenticatorResult`, which includes any query parameters parsed from the callback URI:
```csharp
try
{
WebAuthenticatorResult authResult = await WebAuthenticator.Default.AuthenticateAsync(
new Uri("https://mysite.com/mobileauth/Microsoft"),
new Uri("myapp://"));
string accessToken = authResult?.AccessToken;
// Do something with the token
}
catch (TaskCanceledException e)
{
// Use stopped auth
}
```
The `WebAuthenticator` API takes care of launching the url in the browser and waiting until the callback is received:
:::image type="content" source="media/authentication/web-authenticator.png" alt-text="Typical web authentication flow.":::
If the user cancels the flow at any point, a `TaskCanceledException` is thrown.
### Private authentication session
iOS 13 introduced an ephemeral web browser API for developers to launch the authentication session as private. This enables developers to request that no shared cookies or browsing data is available between authentication sessions and will be a fresh login session each time. This is available through the `WebAuthenticatorOptions` parameter passed to the `AuthenticateAsync` method:
```csharp
try
{
WebAuthenticatorResult authResult = await WebAuthenticator.Default.AuthenticateAsync(
new WebAuthenticatorOptions()
{
Url = new Uri("https://mysite.com/mobileauth/Microsoft"),
CallbackUrl = new Uri("myapp://"),
PrefersEphemeralWebBrowserSession = true
});
string accessToken = authResult?.AccessToken;
// Do something with the token
}
catch (TaskCanceledException e)
{
// Use stopped auth
}
```
## Platform differences
This section describes the platform-specific differences with the web authentication API.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
### [Android](#tab/android)
**Custom Tabs** are used whenever available, otherwise an **Intent** is started for the URL.
# [iOS](#tab/ios)
Depending on the iOS version, the behavior is slightly different:
- iOS 12 or higher\
`ASWebAuthenticationSession` is used.
- iOS 11\
`SFAuthenticationSession` is used.
- Older iOS versions\
`SFSafariViewController` is used if available, otherwise **Safari** is used.
# [Windows](#tab/windows)
> [!CAUTION]
> At the moment, `WebAuthenticator` isn't working on Windows. For more information, see [GitHub issue #2702](https://github.com/dotnet/maui/issues/2702).
On WinUI 3, the `WebAuthenticationBroker` is used, if supported, otherwise the system browser is used.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->
## Apple Sign In
According to [Apple's review guidelines](https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple), if your app uses any social login service to authenticate, it must also offer Apple Sign In as an option. To add Apple Sign In to your apps, first you'll need to configure your app to use Apple Sign In. <!-- TODO link to [configure your app to use Apple Sign In](../ios/platform/ios13/sign-in.md). -->
For iOS 13 and higher, call the `AppleSignInAuthenticator.AuthenticateAsync()` method. This will use automatically the native Apple Sign in APIs so your users get the best experience possible on these devices. For example, you can write your shared code to use the correct API at runtime:
```csharp
var scheme = "..."; // Apple, Microsoft, Google, Facebook, etc.
var authUrlRoot = "https://mysite.com/mobileauth/";
WebAuthenticatorResult result = null;
if (scheme.Equals("Apple")
&& DeviceInfo.Platform == DevicePlatform.iOS
&& DeviceInfo.Version.Major >= 13)
{
// Use Native Apple Sign In API's
result = await AppleSignInAuthenticator.AuthenticateAsync();
}
else
{
// Web Authentication flow
var authUrl = new Uri($"{authUrlRoot}{scheme}");
var callbackUrl = new Uri("myapp://");
result = await WebAuthenticator.Default.AuthenticateAsync(authUrl, callbackUrl);
}
var authToken = string.Empty;
if (result.Properties.TryGetValue("name", out string name) && !string.IsNullOrEmpty(name))
authToken += $"Name: {name}{Environment.NewLine}";
if (result.Properties.TryGetValue("email", out string email) && !string.IsNullOrEmpty(email))
authToken += $"Email: {email}{Environment.NewLine}";
// Note that Apple Sign In has an IdToken and not an AccessToken
authToken += result?.AccessToken ?? result?.IdToken;
```
> [!TIP]
> For non-iOS 13 devices, this will start the web authentication flow, which can also be used to enable Apple Sign In on your Android and Windows devices.
> You can sign into your iCloud account on your iOS simulator to test Apple Sign In.
## ASP.NET core server back end
It's possible to use the `WebAuthenticator` API with any web back-end service. To use it with an ASP.NET core app, configure the web app with the following steps:
01. Set up your [external social authentication providers](/aspnet/core/security/authentication/social/?tabs=visual-studio) in an ASP.NET Core web app.
01. Set the **Default Authentication Scheme** to `CookieAuthenticationDefaults.AuthenticationScheme` in your `.AddAuthentication()` call.
01. Use `.AddCookie()` in your _Startup.cs_ `.AddAuthentication()` call.
01. All providers must be configured with `.SaveTokens = true;`.
```csharp
services.AddAuthentication(o =>
{
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddFacebook(fb =>
{
fb.AppId = Configuration["FacebookAppId"];
fb.AppSecret = Configuration["FacebookAppSecret"];
fb.SaveTokens = true;
});
```
> [!TIP]
> If you'd like to include Apple Sign In, you can use the `AspNet.Security.OAuth.Apple` NuGet package. You can view the full [Startup.cs sample](https://github.com/dotnet/maui/tree/main/src/Essentials/samples/Sample.Server.WebAuthenticator/Startup.cs#L33-L64).
### Add a custom mobile auth controller
With a mobile authentication flow, you usually start the flow directly to a provider the user has chosen. For example, clicking a "Microsoft" button on the sign-in screen of the app. It's also important to return relevant information to your app at a specific callback URI to end the authentication flow.
To achieve this, use a custom API Controller:
```csharp
[Route("mobileauth")]
[ApiController]
public class AuthController : ControllerBase
{
const string callbackScheme = "myapp";
[HttpGet("{scheme}")] // eg: Microsoft, Facebook, Apple, etc
public async Task Get([FromRoute]string scheme)
{
// 1. Initiate authentication flow with the scheme (provider)
// 2. When the provider calls back to this URL
// a. Parse out the result
// b. Build the app callback URL
// c. Redirect back to the app
}
}
```
The purpose of this controller is to infer the scheme (provider) the app is requesting, and start the authentication flow with the social provider. When the provider calls back to the web backend, the controller parses out the result and redirects to the app's callback URI with parameters.
Sometimes you may want to return data such as the provider's `access_token` back to the app, which you can do via the callback URI's query parameters. Or, you may want to instead create your own identity on your server and pass back your own token to the app. What and how you do this part is up to you!
Check out the [full controller sample](https://github.com/dotnet/maui/tree/main/src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs).
> [!NOTE]
> The above sample demonstrates how to return the access token from the 3rd party authentication (ie: OAuth) provider. To obtain a token you can use to authorize web requests to the web backend itself, you should create your own token in your web app, and return that instead. The [Overview of ASP.NET Core authentication](/aspnet/core/security/authentication) has more information about advanced authentication scenarios in ASP.NET Core.

Просмотреть файл

@ -0,0 +1,105 @@
---
title: "Contacts"
description: "Learn how to use the .NET MAUI Contacts class in the Microsoft.Maui.ApplicationModel.Communication namespace, which lets a pick a contact and retrieve information about it."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.Communication"]
---
# Contacts
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IContacts` interface to select a contact and read information about it.
The `IContacts` interface is exposed through the `Contacts.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Contacts` and `IContacts` types are available in the `Microsoft.Maui.ApplicationModel.Communication` namespace.
## Get started
To access the **Contacts** functionality the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
The `ReadContacts` permission is required and must be configured in the Android project. This can be added in the following ways:
- Add the assembly-based permission:
Open the _AssemblyInfo.cs_ file under the **Properties** folder and add:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.ReadContacts)]
```
\- or -
- Update the Android Manifest:
Open the _AndroidManifest.xml_ file under the **Properties** folder and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.READ_CONTACTS" />
```
<!-- TODO not yet supported>
\- or -
- Use the Android project properties:
Right-click on the Android project and open the project's properties. Under **Android Manifest** find the **Required permissions:** area and check the **Contacts** permission. This will automatically update the **AndroidManifest.xml** file.
-->
# [iOS](#tab/ios)
In the **Solution Explorer** pane, right-click on the _Platforms/iOS/Info.plist_ file. Select **Open With** and then select the **XML (Text) Editor** item. Press the **OK** button. In the file, add the following key and value:
```xml
<key>NSContactsUsageDescription</key>
<string>This app needs access to contacts to pick a contact and get info.</string>
```
The `<string>` element is the description specific to your app and is shown to the user.
# [Windows](#tab/windows)
In the **Solution Explorer** pane, right-click on the _Platforms/Windows/Package.appxmanifest_ file, and select **View Code**. Under the `<Capabilities>` node, add `<uap:Capability Name="contacts"/>`.
-----
<!-- markdownlint-enable MD025 -->
## Pick a contact
You can request the user to pick a contact by calling the `PickContactAsync` method. A contact dialog will appear on the device allowing the user to select a contact. If the user doesn't select a contact, `null` is returned.
:::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="contact_select":::
## Get all contacts
The `GetAllAsync` method returns a collection of contacts.
:::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="contact_all":::
## Platform differences
This section describes the platform-specific differences with the contacts API.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
# [Android](#tab/android)
- The `cancellationToken` parameter in the `GetAllAsync` method isn't supported.
# [iOS](#tab/ios)
- The `cancellationToken` parameter in the `GetAllAsync` method isn't supported.
- The iOS platform doesn't support the `DisplayName` property natively, thus, the `DisplayName` value is constructed as "GivenName FamilyName".
# [Windows](#tab/windows)
No platform differences.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,91 @@
---
title: "Email"
description: "Learn how to use the .NET MAUI Email class in the Microsoft.Maui.ApplicationModel.Communication namespace to open the default email application. The subject, body, and recipients of an email can be set."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.Communication"]
---
# Email
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IEmail` interface to open the default email app. When the email app is loaded, it can be set to create a new email with the specified recipients, subject, and body. The `IEmail` interface is exposed through the `Email.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Email` and `IEmail` types are available in the `Microsoft.Maui.ApplicationModel.Communication` namespace.
## Get started
To access the email functionality, the following platform specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
If your project's Target Android version is set to **Android 11 (R API 30)** or higher, you must update your _Android Manifest_ with queries that use Android's [package visibility requirements](https://developer.android.com/preview/privacy/package-visibility).
In the _Platforms/Android/AndroidManifest.xml_ file, add the following `queries/intent` nodes the `manifest` node:
```xml
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries>
```
# [iOS](#tab/ios)
Apple requires that you define the schemes you want to use. Add the `LSApplicationQueriesSchemes` key and schemes to the _Platforms/iOS/Info.plist_ file:
```xml
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mailto</string>
</array>
```
# [Windows](#tab/windows)
No additional setup required.
-----
<!-- markdownlint-enable MD025 -->
## Using Email
The Email functionality works by providing the email information as an argument to the `ComposeAsync` method. In this example, the `EmailMessage` type is used to represent the email information:
:::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="email_compose":::
## File attachments
When creating the email provided to the email client, you can add file attachments. The file type (MIME) is automatically detected, so you don't need to specify it. Some mail clients may restrict the types of files you send, or possibly prevent attachments altogether.
Use the `EmailMessage.Attachments` collection to manage the files attached to an email.
The following example demonstrates adding arbitrary text to a file, and then adding it to the email.
:::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="email_picture" highlight="18":::
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
# [Android](#tab/android)
Not all email clients for Android support `EmailBodyFormat.Html`, since there is no way to detect this, we recommend using `EmailBodyFormat.PlainText` when sending emails.
# [iOS](#tab/ios)
Both `EmailBodyFormat.Html` and `EmailBodyFormat.PlainText` are supported.
> [!WARNING]
> To use the email API on iOS, you must run it on a physical device. Otherwise, an exception is thrown.
# [Windows](#tab/windows)
Only supports `EmailBodyFormat.PlainText` as the EmailBodyFormat.`BodyFormat`. Attempting to send an `Html` email throws the exception: `FeatureNotSupportedException`.
Not all email clients support sending attachments. <!-- For more information, see [Sending emails](/windows/uwp/contacts-and-calendar/sending-email).-->
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 58 KiB

Просмотреть файл

@ -0,0 +1,84 @@
---
title: "Connectivity"
description: "Learn how to use the .NET MAUI Connectivity interface in the Microsoft.Maui.Networking namespace. With this interface, you can determine if you can communicate with the internet and which network devices are connected"
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Networking", "Connectivity"]
---
# Connectivity
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IConnectivity` interface to inspect the network accessibility of the device. The network connection may have access to the internet. Devices also contain different kinds of network connections, such as Bluetooth, cellular, or WiFi. The `IConnectivity` interface has an event to monitor changes in the devices connection state. The `IConnectivity` interface is exposed through the `Connectivity.Current` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Connectivity` and `IConnectivity` types are available in the `Microsoft.Maui.Networking` namespace.
## Get started
To access the **Connectivity** functionality, the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
The `AccessNetworkState` permission is required and must be configured in the Android project. This can be added in the following ways:
- Add the assembly-based permission:
Open the _Platforms/Android/MainApplication.cs_ file and add the following assembly attributes after `using` directives:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.AccessNetworkState)]
```
\- or -
- Update the Android Manifest:
Open the _Platforms/Android/AndroidManifest.xml_ file and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```
<!-- NOT SUPPORTED
\- or -
- Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the **Access Network State** permission. This will automatically update the _AndroidManifest.xml_ file.
-->
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Using Connectivity
You can determine the scope of the current network by checking the `NetworkAccess` property.
:::code language="csharp" source="../snippets/shared_1/NetworkingPage.cs" id="network_test":::
Network access falls into the following categories:
- `Internet` &mdash; Local and internet access.
- `ConstrainedInternet` &mdash; Limited internet access. This value means that there's a captive portal, where local access to a web portal is provided. Once the portal is used to provide authentication credentials, internet access is granted.
- `Local` &mdash; Local network access only.
- `None` &mdash; No connectivity is available.
- `Unknown` &mdash; Unable to determine internet connectivity.
You can check what type of connection profile the device is actively using:
:::code language="csharp" source="../snippets/shared_1/NetworkingPage.cs" id="network_profiles":::
Whenever the connection profile or network access changes, the `ConnectivityChanged` event is raised:
:::code language="csharp" source="../snippets/shared_1/NetworkingPage.cs" id="network_implementation":::
## Limitations
It's important to know that it's possible that `Internet` is reported by `NetworkAccess` but full access to the web isn't available. Because of how connectivity works on each platform, it can only guarantee that a connection is available. For instance, the device may be connected to a Wi-Fi network, but the router is disconnected from the internet. In this instance `Internet` may be reported, but an active connection isn't available.

Просмотреть файл

@ -0,0 +1,51 @@
---
title: "Phone dialer"
description: "Learn how to open the phone dialer to a specific number, in .NET MAUI. The PhoneDialer class in the Microsoft.Maui.ApplicationModel.Communication namespace is used to open the phone dialer."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.Communication"]
---
# Phone dialer
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IPhoneDialer` interface. This interface enables an application to open a phone number in the dialer. The `IPhoneDialer` interface is exposed through the `PhoneDialer.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `PhoneDialer` and `IPhoneDialer` types are available in the `Microsoft.Maui.ApplicationModel.Communication` namespace.
## Get started
To access the phone dialer functionality, the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
If your project's Target Android version is set to **Android 11 (R API 30)** or higher, you must update your _Android Manifest_ with queries that use Android's [package visibility requirements](https://developer.android.com/preview/privacy/package-visibility).
In the _Platforms/Android/AndroidManifest.xml_ file, add the following `queries/intent` nodes the `manifest` node:
```xml
<queries>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel"/>
</intent>
</queries>
```
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Open the phone dialer
The phone dialer functionality works by calling the `Open` method with a phone number. When the phone dialer is opened, .NET MAUI will automatically attempt to format the number based on the country code, if specified.
:::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="phone_dial":::

Просмотреть файл

@ -0,0 +1,51 @@
---
title: "SMS"
description: "Learn how to use the .NET MAUI Sms class in Microsoft.Maui.ApplicationModel.Communication to open the default SMS application. The text message can be preloaded with a message and recipient."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.Communication"]
---
# SMS
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `ISms` interface to open the default SMS app and preload it with a message and recipient. The `ISms` interface is exposed through the `Sms.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Sms` and `ISms` types are available in the `Microsoft.Maui.ApplicationModel.Communication` namespace.
## Get started
To access the SMS functionality, the following platform specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
If your project's Target Android version is set to **Android 11 (R API 30)** or higher, you must update your _Android Manifest_ with queries that use Android's [package visibility requirements](https://developer.android.com/preview/privacy/package-visibility).
In the _Platforms/Android/AndroidManifest.xml_ file, add the following `queries/intent` nodes the `manifest` node:
```xml
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="smsto"/>
</intent>
</queries>
```
# [iOS](#tab/ios)
No additional setup required.
# [Windows](#tab/windows)
No additional setup required.
-----
<!-- markdownlint-enable MD025 -->
## Create a message
The SMS functionality works by creating a new `SmsMessage` object, and calling the `ComposeAsync` method. You can optionally include a message and zero or more recipients.
:::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="sms_send":::

Просмотреть файл

@ -0,0 +1,41 @@
---
title: "Clipboard"
description: "Learn how to use the .NET MAUI Clipboard class in the Microsoft.Maui.ApplicationModel.DataTransfer namespace, which lets you copy and paste text to the system clipboard."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.DataTransfer"]
---
# Clipboard
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `Clipboard` class. With this class, you can copy and paste text to and from the system clipboard.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Clipboard` class is available in the `Microsoft.Maui.ApplicationModel.DataTransfer` namespace.
> [!TIP]
> Access to the clipboard must be done on the main user interface thread. For more information on how to invoke methods on the main user interface thread, see [MainThread](../appmodel/main-thread.md).
## Using Clipboard
The clipboard is accessed through default implementation of the `IClipboard` interface, available from the `Microsoft.Maui.ApplicationModel.DataTransfer.Clipboard.Default` property. Access to the clipboard is limited to string data. You can check if the clipboard contains data, set or clear the data, and read the data. The `ClipboardContentChanged` event is raised whenever the clipboard data changes.
The following code example demonstrates using a button to set the clipboard data:
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="clipboard_set":::
The following code example demonstrates using a button to read the clipboard data. The code first checks if the clipboard has data, read that data, and then uses a `null` value with `SetTextAsync` to clear the clipboard:
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="clipboard_read":::
## Clear the clipboard
You can clear the clipboard by passing `null` to the `SetTextAsync` method, as the following code example demonstrates:
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="clipboard_clear":::
## Detecting clipboard changes
The `IClipboard` interface provides the `ClipboardContentChanged` event. When this event is raised, the clipboard content has changed. The following code example adds a handler to the event when the content page is loaded:
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="clipboard_event":::

Двоичные данные
docs/platform-integration/data/media/share/share.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 111 KiB

Просмотреть файл

@ -0,0 +1,93 @@
---
title: "Share"
description: "Learn how to use the .NET MAUI Share class, which can share data, such as web links, to other applications on the device."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.DataTransfer"]
---
# Share
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `Share` class. This class provides an API to send date, such as text or web links, to the devices share function.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Share` class is available in the `Microsoft.Maui.ApplicationModel.DataTransfer` namespace.
When a share request is made, the device displays a share window, prompting the user to choose an app to share with:
:::image type="content" source="media/share/share.png" alt-text="Share from your app to a different app":::
## Get started
To access the **Share** functionality, the following platform-specific setup is required:
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
No setup is required.
# [iOS](#tab/ios)
If your application is going to share media files, such as photos and videos, you must add the following keys to your _Info.plist_ file:
```xml
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs access to the photo gallery to save photos and videos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to the photo gallery to save photos and videos.</string>
```
The `<string>` elements represent the text shown to your users when permission is requested. Make sure that you change the text to something specific to your application.
# [Windows](#tab/windows)
No setup is required.
-----
## Share text and links
The share functionality works by calling the `RequestAsync` method with a data payload that includes information to share to other applications. `Text` and `Uri` can be mixed and each platform will handle filtering based on content.
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="share_text_uri":::
## Share a file
You can also share files to other applications on the device. .NET MAUI automatically detects the file type (MIME) and requests a share. However, operating systems may restrict which types of files can be shared.
The following code example writes a text file to the device, and then requests a share:
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="share_file":::
## Share multiple files
Sharing multiple files is slightly different from sharing a single file. Instead of using the `File` property of the share request, use the `Files` property:
:::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="share_file_multiple":::
## Presentation location
[!INCLUDE [ios-PresentationSourceBounds](../includes/ios-PresentationSourceBounds.md)]
## Platform differences
This section describes the platform-specific differences with the share API.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
# [Android](#tab/android)
- The `Subject` property is used for the desired subject of a message.
# [iOS](#tab/ios)
- The `Subject` property isn't used.
# [Windows](#tab/windows)
- The `Title` property will default to the application name if not set.
- The `Subject` property isn't used.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,117 @@
---
title: "Media picker"
description: "Learn how to use the MediaPicker class in the Microsoft.Maui.Media namespace, to prompt the user to select or take a photo or video."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Media", "MediaPicker"]
---
# Media picker
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IMediaPicker` interface. This interfaces lets a user pick or take a photo or video on the device. The `IMediaPicker` interface is exposed through the `MediaPicker.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `MediaPicker` and `IMediaPicker` types are available in the `Microsoft.Maui.Media` namespace.
## Get started
To access the media picker functionality, the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
The `CAMERA`, `WRITE_EXTERNAL_STORAGE`, `READ_EXTERNAL_STORAGE` permissions are required, and must be configured in the Android project. These can be added in the following ways:
- Add the assembly-based permission:
Open the _Platforms/Android/MainApplication.cs_ file and add the following assembly attributes after `using` directives:
:::code language="csharp" source="../snippets/shared_1/Platforms/Android/MainApplication.cs" id="media_picker":::
\- or -
- Update the Android Manifest:
Open the _Platforms/Android/AndroidManifest.xml_ file and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
```
<!-- NOT SUPPORTED
\- or -
- Use the Android project properties:
Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the appropriate permissions. This will automatically update the _AndroidManifest.xml_ file.
-->
If your project's Target Android version is set to **Android 11 (R API 30)** or higher, you must update your _Android Manifest_ with queries that use Android's [package visibility requirements](https://developer.android.com/preview/privacy/package-visibility).
In the _Platforms/Android/AndroidManifest.xml_ file, add the following `queries/intent` nodes the `manifest` node:
```xml
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
```
# [iOS](#tab/ios)
In the _Platforms/iOS/Info.plist_ file, add the following keys and values:
```xml
<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for taking videos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs access to the photo gallery for picking photos and videos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos gallery for picking photos and videos.</string>
```
Each `<string>` element represents the reason the app is requesting access to that specific permission. This text is shown to the user.
# [Windows](#tab/windows)
<!-- NOT SUPPORTED>
In the `Package.appxmanifest` under **Capabilities** ensure that `Microphone` and `Webcam` capabilities are checked.
-->
In the **Solution Explorer** pane, right-click on the _Platforms/Windows/Package.appxmanifest_ file, and select **View Code**. Under the `<Capabilities>` node, add `<uap:Capability Name="microphone"/>` and `<uap:Capability Name="webcam"/>` elements.
-----
<!-- markdownlint-enable MD025 -->
## Using media picker
The `IMediaPicker` interface has the following methods that all return a `FileResult`, which can be used to get the file's location or read it.
- `PickPhotoAsync`\
Opens the media browser to select a photo.
- `CapturePhotoAsync`\
Opens the camera to take a photo.
- `PickVideoAsync`\
Opens the media browser to select a video.
- `CaptureVideoAsync`\
Opens the camera to take a video.
Each method optionally takes in a `MediaPickerOptions` parameter type that allows the `Title` to be set on some operating systems, which is displayed to the user.
> [!IMPORTANT]
> All methods must be called on the UI thread because permission checks and requests are automatically handled by .NET MAUI.
## Take a photo
Call the `CapturePhotoAsync` method to open the camera and let the user take a photo. If the user takes a photo, the return value of the method will be a non-null value. The following code sample uses the media picker to take a photo and save it to the cache directory:
:::code language="csharp" source="../snippets/shared_1/MediaPage.cs" id="photo_take_and_save":::
[!INCLUDE [tip-file-result](../includes/tip-file-result.md)]

Просмотреть файл

@ -0,0 +1,24 @@
---
title: "Screenshot"
description: "Learn how to use the Screenshot class in the Microsoft.Maui.Media namespace, to capture of the current displayed screen of the app."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Media", "ScreenShot"]
---
# Screenshot
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IScreenshot` interface. This interfaces lets you take a capture of the current displayed screen of the app. The `IScreenshot` interface is exposed through the `Screenshot.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Screenshot` and `IScreenshot` types are available in the `Microsoft.Maui.Media` namespace.
## Capture a screenshot
To capture a screenshot of the current app, use the `CaptureAsync` method. This method returns a `IScreenshotResult`, which contains information about the capture, such as the width and height of the screenshot. `IScreenshotResult` also includes a `Stream` property that is used to convert the screenshot into an image object for use by your app. The following example demonstrates a method that captures a screenshot and returns it as an `ImageSource`.
:::code language="csharp" source="../snippets/shared_1/MediaPage.cs" id="screenshot":::
## Limitations
Not all views support being captured at a screen level, such as an OpenGL view.

Просмотреть файл

@ -0,0 +1,46 @@
---
title: "Text-to-Speech"
description: "Learn how to use the .NET MAUI TextToSpeech class, which enables an application utilize the built in text-to-speech engines to speak back text from the device."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Media", "TextToSpeech"]
---
# Text-to-Speech
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `ITextToSpeech` interface. This interface enables an application to utilize the built-in text-to-speech engines to speak back text from the device. You can also use it to query for available languages. The `ITextToSpeech` interface is exposed through the `TextToSpeech.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `TextToSpeech` and `ITextToSpeech` types are available in the `Microsoft.Maui.Media` namespace.
## Using Text-to-Speech
Text-to-speech works by calling the `SpeakAsync` method with the text to speak, as the following code example demonstrates:
:::code language="csharp" source="../snippets/shared_1/MediaPage.cs" id="speak":::
This method takes in an optional `CancellationToken` to stop the utterance once it starts.
:::code language="csharp" source="../snippets/shared_1/MediaPage.cs" id="speak_cancel":::
Text-to-Speech will automatically queue speech requests from the same thread.
:::code language="csharp" source="../snippets/shared_1/MediaPage.cs" id="speak_queue":::
## Settings
To control the volume, pitch, and locale of the voice, use the `SpeechOptions` class. Pass an instance of that class to the `SpeakAsync` method. the `GetLocalesAsync` method retrieves a collection of the locales provided by the operating system.
:::code language="csharp" source="../snippets/shared_1/MediaPage.cs" id="speak_options":::
The following are supported values for these parameters:
| Parameter | Minimum | Maximum |
|-----------|:-------:|:-------:|
| `Pitch` | 0 | 2.0 |
| `Volume` | 0 | 1.0 |
## Limitations
- Utterance queueing is not guaranteed if called across multiple threads.
- Background audio playback is not officially supported.

Просмотреть файл

@ -0,0 +1,50 @@
---
title: "Unit converters"
description: "Learn how to use the .NET MAUI UnitConverters class, which provides several unit converters to help developers."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Media", "UnitConverters"]
---
# Unit converters
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `UnitConverters` class. This class provides several unit converters to help developers convert from one unit of measurement to another.
## Using unit converters
All unit converters are available by using the static `Microsoft.Maui.Media.UnitConverters` class. For example, you can convert Fahrenheit to Celsius with the `FahrenheitToCelsius` method:
```csharp
var celsius = UnitConverters.FahrenheitToCelsius(32.0);
```
Here is a list of available conversions:
- `FahrenheitToCelsius`
- `CelsiusToFahrenheit`
- `CelsiusToKelvin`
- `KelvinToCelsius`
- `MilesToMeters`
- `MilesToKilometers`
- `KilometersToMiles`
- `MetersToInternationalFeet`
- `InternationalFeetToMeters`
- `DegreesToRadians`
- `RadiansToDegrees`
- `DegreesPerSecondToRadiansPerSecond`
- `RadiansPerSecondToDegreesPerSecond`
- `DegreesPerSecondToHertz`
- `RadiansPerSecondToHertz`
- `HertzToDegreesPerSecond`
- `HertzToRadiansPerSecond`
- `KilopascalsToHectopascals`
- `HectopascalsToKilopascals`
- `KilopascalsToPascals`
- `HectopascalsToPascals`
- `AtmospheresToPascals`
- `PascalsToAtmospheres`
- `CoordinatesToMiles`
- `CoordinatesToKilometers`
- `KilogramsToPounds`
- `PoundsToKilograms`
- `StonesToPounds`
- `PoundsToStones`

Просмотреть файл

@ -0,0 +1,119 @@
---
title: "Battery"
description: "Learn how to use the .NET MAUI Battery class in the Microsoft.Maui.Devices namespace. You can check the device's battery information and monitor for changes."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices"]
---
# Battery
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `Battery` class to check the device's battery information and monitor for changes. This class also provides information about the device's energy-saver status, which indicates if the device is running in a low-power mode.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Battery` class is available in the `Microsoft.Maui.Devices` namespace.
## Get started
To access the **Battery** functionality the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
The `Battery` permission is required and must be configured in the Android project. This can be added in the following ways:
- Add the assembly-based permission:
Open the _AssemblyInfo.cs_ file under the **Properties** folder and add:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.BatteryStats)]
```
\- or -
- Update the Android Manifest:
In the **Solution Explorer**, open the _AndroidManifest.xml_ file. This is typically located in the **Your-project** > **Platforms** > **Android** folder. Add the following node as a child to the `<manifest>` node:
```xml
<uses-permission android:name="android.permission.BATTERY_STATS" />
```
<!-- TODO not yet supported>
\- or -
- Use the Android project properties:
Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the **Battery** permission. This will automatically update the _AndroidManifest.xml_ file.
-->
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Check the battery status
The battery status can be checked by accessing the default implementation of the `IBattery` interface. This implementation is available from the `Microsoft.Maui.Devices.Battery.Default` property. The interface defines the `BatteryInfoChanged` event which is raised when the state of the battery changed. Outside of this event, you can still check the state of the battery at any time by using the various properties defined by the interface.
The following example demonstrates how to use the monitor the `BatteryInfoChanged` event and report the battery status two `Label` controls:
:::code language="csharp" source="../snippets/shared_1/BatteryTestPage.xaml.cs" id="watch_battery":::
The `ChargeLevel` property returns a value between **0.0** and **1.0**, indicating the battery's charge level from empty to full, respectively.
## Low-power energy-saver mode
Devices that run on batteries can be put into a low-power energy-saver mode. Sometimes devices are switched into this mode automatically, like when the battery drops below 20% capacity. The operating system responds to energy-saver mode by reducing activities that tend to deplete the battery. Applications can help by avoiding background processing or other high-power activities when energy-saver mode is on.
> [!IMPORTANT]
> Applications should avoid background processing if the device's energy-saver status is on.
The energy-saver status of the device can be read by accessing the `EnergySaverStatus` property, which is either `On`, `Off`, or `Unknown`. If the status is `On`, the application should avoid background processing or other activities that may consume a lot of power.
The battery will raise the `EnergySaverStatusChanged` event when the battery enters or leaves energy-saver mode.
You can also obtain the current energy-saver status of the device using the static `Battery.EnergySaverStatus` property:
The following code example monitors the energy-saver status and sets a property accordingly.
:::code language="csharp" source="../snippets/shared_1/BatteryTestPage.xaml.cs" id="energy_saver":::
## Power source
The `PowerSource` property returns a `BatteryPowerSource` enumeration that indicates how the device is being charged, if at all. If it's not being charged, the status will be `Battery`. The `AC`, `Usb`, and `Wireless` values indicate that the battery is being charged.
The following code example sets the text of a `Label` control based on power source.
:::code language="csharp" source="../snippets/shared_1/BatteryTestPage.xaml.cs" id="charge_mode":::
## Platform differences
This section describes the platform-specific differences with the battery.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
# [Android](#tab/android)
No platform differences.
# [iOS](#tab/ios)
- APIs won't work in a simulator and you must use a real device.
- Only returns `AC` or `Battery` for `PowerSource`.
# [Windows](#tab/windows)
- Only returns `AC` or `Battery` for `PowerSource`.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,50 @@
---
title: "Device Display Information"
description: "Learn how to use the .NET MAUI DeviceDisplay class in the Microsoft.Maui.Devices namespace, which provides screen metrics for the device on which the app is running."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices"]
---
# Device display information
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `DeviceDisplay` class to read information about the device's screen metrics. This class can be used to request the screen stays awake while the app is running.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `DeviceDisplay` class is available in the `Microsoft.Maui.Devices` namespace.
## Main display info
Information about your device's screen is accessed by the default implementation of the `IDeviceDisplay` interface, which is available by accessing the `Microsoft.Maui.Devices.DeviceDisplay.Current` property.
The `IDeviceDisplay.MainDisplayInfo` property returns information about the screen and orientation. The following code example uses the `Loaded` event of a page to read information about the current screen:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="main_display":::
The `IDeviceDisplay` interface also provides the `MainDisplayInfoChanged` event that is raised when any screen metric changes, such as when the device orientation changes from `DisplayOrientation.Landscape` to `DisplayOrientation.Portrait`.
## Keep the screen on
You can also prevent the device from locking or the screen turning off by setting the `IDeviceDisplay.KeepScreenOn` property to `true`. The following code example toggles the screen lock whenever the switch control is pressed:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="always_on":::
## Platform differences
This section describes the platform-specific differences with the device display.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
No platform differences.
# [iOS](#tab/ios)
- Accessing `DeviceDisplay` must be done on the UI thread or else an exception will be thrown. You can use the [`MainThread.BeginInvokeOnMainThread`](../appmodel/main-thread.md) method to run that code on the UI thread.
# [Windows](#tab/windows)
No platform differences.
-----
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,107 @@
---
title: "Flashlight"
description: "Learn how to use the .NET MAUI Flashlight class in the Microsoft.Maui.Devices namespace. This class provides the ability to turn on or off the device's camera flash, to emulate a flashlight."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices"]
---
# Flashlight
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `Flashlight` class. With this class, you can toggle the device's camera flash on and off, to emulate a flashlight.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Flashlight` class is available in the `Microsoft.Maui.Devices` namespace.
## Get started
To access the flashlight functionality the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
There are two permissions to configure in your project: `Flashlight` and `Camera`. These permissions can be set in the following ways:
- Add the assembly-based permission:
Open the _AssemblyInfo.cs_ file under the **Properties** folder and add:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.Flashlight)]
[assembly: UsesPermission(Android.Manifest.Permission.Camera)]
```
\- or -
- Update the Android Manifest:
Open the _AndroidManifest.xml_ file under the **Properties** folder and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.CAMERA" />
```
<!-- TODO unsupported right now
\- or -
- Use the Android project properties:
<!-- TODO: Check on this value
Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the **FLASHLIGHT** and **CAMERA** permissions. This will automatically update the _AndroidManifest.xml_ file.
-->
By adding these permissions, [Google Play will automatically filter out devices](https://developer.android.com/guide/topics/manifest/uses-feature-element.html#permissions-features) without specific hardware. You can get around this by adding the following to your _AssemblyInfo.cs_ file in your Android project:
```csharp
[assembly: UsesFeature("android.hardware.camera", Required = false)]
[assembly: UsesFeature("android.hardware.camera.autofocus", Required = false)]
```
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Use Flashlight
The flashlight is accessed through default implementation of the `IFlashlight` interface, available from the `Microsoft.Maui.Devices.Flashlight.Default` property. The flashlight can be turned on and off through the `TurnOnAsync` and `TurnOffAsync` methods. The following code example ties the flashlight's on or off state to a `Switch` control:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="flashlight":::
## Platform differences
This section describes the platform-specific differences with the flashlight.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
### [Android](#tab/android)
The `Flashlight` class has been optimized based on the device's operating system.
#### API level 23 and higher
On newer API levels, [Torch Mode](https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#setTorchMode) will be used to turn on or off the flash unit of the device.
#### API level 22 and lower
A camera surface texture is created to turn on or off the `FlashMode` of the camera unit.
### [iOS](#tab/ios)
The `AVCaptureDevice` API is used to turn on and off the Torch and Flash mode of the device.
### [Windows](#tab/windows)
The <xref:Windows.Devices.Lights.Lamp> API is used to turn on or off the first detected lamp on the back of the device.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,56 @@
---
title: "Geocoding"
description: "Learn how to use the .NET MAUI Geocoding class in the Microsoft.Maui.Devices.Sensors namespace. This class provides APIs to both geocode a placemark to a positional coordinate, and reverse geocode coordinates to a placemark."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices", "Microsoft.Maui.Devices.Sensors"]
---
# Geocoding
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IGeocoding` interface. This interfaces provides APIs to geocode a placemark to a positional coordinates and reverse geocode coordinates to a placemark. The `IGeocoding` interface is exposed through the `Geocoding.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Geocoding` and `IGeocoding` types are available in the `Microsoft.Maui.Devices.Sensors` namespace.
## Get started
To access the **Geocoding** functionality the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
No setup is required.
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
A Bing Maps API key is required to use geocoding functionality. Sign up for a free [Bing Maps](https://www.bingmapsportal.com/) account. Under **My account** > **My keys**, create a new key and fill out information based on your application type, which should be **Windows Application**.
To enable geocoding functionality in your app, add the `ConfigureEssentials` step to the `CreateMauiApp` bootstrap code. The app startup code is configured in the _MauiProgram.cs_ file. Call the `UseMapServiceToken` method to enable geocoding functionality:
:::code language="csharp" source="../snippets/shared_1/MauiProgram.cs" id="bootstrap_maptoken" highlight="12-15":::
-----
<!-- markdownlint-enable MD025 -->
## Use geocoding
The following example demonstrates how to get the location coordinates for an address:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="geocoding_location":::
The altitude isn't always available. If it isn't available, the `Altitude` property might be `null`, or the value might be `0`. If the altitude is available, the value is in meters above sea level.
## Reverse geocoding
Reverse geocoding is the process of getting placemarks for an existing set of coordinates. The following example demonstrates getting placemarks:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="geocoding_reverse":::
## Get the distance between two locations
The `Location` and `LocationExtensions` classes define methods to calculate the distance between two locations. For an example of getting the distance between two locations, see [Distance between two locations](geolocation.md#distance-between-two-locations).

Просмотреть файл

@ -0,0 +1,208 @@
---
title: "Geolocation"
description: "Learn how to use the .NET MAUI Geolocation class in the Microsoft.Maui.Devices.Sensors namespace. This class provides API to retrieve the device's current geolocation coordinates."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices", "Microsoft.Maui.Devices.Sensors"]
---
# Geolocation
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `IGeolocation` class. This class provides APIs to retrieve the device's current geolocation coordinates. The `IGeolocation` interface is exposed through the `Geolocation.Default` property.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `Geolocation` and `IGeolocation` types are available in the `Microsoft.Maui.Devices.Sensors` namespace.
## Get started
To access the **Geolocation** functionality, the following platform-specific setup is required:
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
**Coarse and Fine Location** permissions are required and must be configured in the Android project. Additionally, if your app targets Android 5.0 (API level 21) or higher, you must declare that your app uses the hardware features in the manifest file. This can be added in the following ways:
- Add the assembly-based permission:
Open the _Platforms/Android/MainApplication.cs_ file and add the following assembly attributes after `using` directives:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]
[assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)]
[assembly: UsesFeature("android.hardware.location", Required = false)]
[assembly: UsesFeature("android.hardware.location.gps", Required = false)]
[assembly: UsesFeature("android.hardware.location.network", Required = false)]
```
If your application is targeting Android 10 - Q (API Level 29 or higher) and is requesting `LocationAlways`, you must also add this permission request:
```csharp
[assembly: UsesPermission(Manifest.Permission.AccessBackgroundLocation)]
```
\- or -
- Update the Android Manifest:
Open the _Platforms/Android/AndroidManifest.xml_ file and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
```
If your application is targeting Android 10 - Q (API Level 29 or higher) and is requesting `LocationAlways`, you must also add this permission request:
```xml
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
```
<!-- NOT SUPPORTED
\- or -
- Use the Android project properties:
Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the **ACCESS_COARSE_LOCATION** and **ACCESS_FINE_LOCATION** permissions. This will automatically update the _AndroidManifest.xml_ file.
-->
> [!TIP]
> Be sure to read the [Android documentation on background location updates](https://developer.android.com/training/location/permissions), as there are many restrictions that need to be considered.
# [iOS](#tab/ios)
In the _Platforms/iOS/Info.plist_ file, add the following keys and values:
```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>Fill in a reason why your app needs access to location.</string>
```
The `<string>` element is the reason the app is requesting access to location information. This text is shown to the user.
<!-- NOT SUPPORTED
An alternative to editing the _Info.plist_ file directly is opening the plist editor. In the editor you can add the **Privacy - Location When In Use Usage Description** property, and fill in a value to display to the user.
-->
# [Windows](#tab/windows)
<!-- NOT SUPPORTED>
In the `Package.appxmanifest` under **Capabilities** ensure that `Location` capability are checked.
-->
In the **Solution Explorer** pane, right-click on the _Platforms/Windows/Package.appxmanifest_ file, and select **View Code**. Under the `<Capabilities>` node, add the `<uap:Capability Name="location"/>` element.
-----
<!-- markdownlint-enable MD025 -->
## Get the last known location
The device may have cached the most recent location of the device. Use the `GetLastKnownLocationAsync` method to access the cached location, if available. This is often faster then doing a full location query, but can be less accurate. If no cached location exists, this method returns `null`.
> [!NOTE]
> When necessary, the Geolocation API prompts the user for permissions.
The following code example demonstrates checking for a cached location:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="geolocation_cached":::
Depending on the device, not all location values may be available. For example, the `Altitude` property might be `null`, have a value of 0, or have a positive value indicating the meters above sea level. Other values that may not be present include the `Speed` and `Course` properties.
## Get the current location
While checking for the [last known location](#get-the-last-known-location) of the device may be quicker, it can be inaccurate. Use the `GetLocationAsync` method to query the device for the current location. You can configure the accuracy and timeout of the query. It's best to the method overload that uses the `GeolocationRequest` and `CancellationToken` parameters, since it may take some time to get the device's location.
> [!NOTE]
> When necessary, the Geolocation API prompt's the user for permissions.
The following code example demonstrates how to request the device's location, while supporting cancellation:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="geolocation_get":::
Not all location values may be available, depending on the device. For example, the `Altitude` property might be `null`, have a value of 0, or have a positive value indicating the meters above sea level. Other values that may not be present include `Speed` and `Course`.
## Accuracy
The following sections outline the location accuracy distance, per platform:
### Lowest
| Platform | Distance (in meters) |
|----------|----------------------|
| Android | 500 |
| iOS | 3000 |
| Windows | 1000 - 5000 |
### Low
| Platform | Distance (in meters) |
|----------|----------------------|
| Android | 500 |
| iOS | 1000 |
| Windows | 300 - 3000 |
### Medium (Default)
| Platform | Distance (in meters) |
|----------|----------------------|
| Android | 100 - 500 |
| iOS | 100 |
| Windows | 30-500 |
### High
| Platform | Distance (in meters) |
|----------|----------------------|
| Android | 0 - 100 |
| iOS | 10 |
| Windows | <= 10 |
### Best
| Platform | Distance (in meters) |
|----------|----------------------|
| Android | 0 - 100 |
| iOS | ~0 |
| Windows | <= 10 |
## Detecting mock locations
Some devices may return a mock location from the provider or by an application that provides mock locations. You can detect this by using the `IsFromMockProvider` on any `Location`:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="geolocation_ismock":::
## Distance between two locations
The `Location.CalculateDistance` method calculates the distance between two geographic locations. This calculated distance doesn't take roads or other pathways into account, and is merely the shortest distance between the two points along the surface of the Earth. This calculation is known as the _great-circle distance_ calculation.
The following code calculates the distance between the United States of America cities of Boston and San Francisco:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="geolocation_distance":::
The `Location` constructor accepts the latitude and longitude arguments, respectively. Positive latitude values are north of the equator, and positive longitude values are east of the Prime Meridian. Use the final argument to `CalculateDistance` to specify miles or kilometers. The `UnitConverters` class also defines `KilometersToMiles` and `MilesToKilometers` methods for converting between the two units.
## Platform differences
This section describes the platform-specific differences with the geolocation API.
Altitude is calculated differently on each platform.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
# [Android](#tab/android)
On Android, [altitude](https://developer.android.com/reference/android/location/Location#getAltitude()), if available, is returned in meters above the WGS 84 reference ellipsoid. If this location doesn't have an altitude, `0.0` is returned.
# [iOS](#tab/ios)
On iOS, [altitude](https://developer.apple.com/documentation/corelocation/cllocation/1423820-altitude) is measured in meters. Positive values indicate altitudes above sea level, while negative values indicate altitudes below sea level.
# [Windows](#tab/windows)
On Windows, altitude is returned in meters. <!-- For more information, see the [AltitudeReferenceSystem](/uwp/api/windows.devices.geolocation.geopoint.altitudereferencesystem#Windows_Devices_Geolocation_Geopoint_AltitudeReferenceSystem) reference documentation.-->
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,68 @@
---
title: "Haptic Feedback"
description: "Learn how to use the .NET MAUI HapticFeedback class in the Microsoft.Maui.Devices namespace. This class lets you control haptic feedback on a device."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices"]
---
# Haptic feedback
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `HapticFeedback` class to control haptic feedback on a device. Haptic feedback is generally manifested by a gentle vibration sensation provided by the device to give a response to the user. Some examples of haptic feedback are when a user types on a virtual keyboard or when they play a game where the player's character has an encounter with an enemy character.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `HapticFeedback` class is available in the `Microsoft.Maui.Devices` namespace.
## Get started
To access the haptic feedback functionality, the following platform-specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
The `Vibrate` permission is required and must be configured in the Android project. This can be added in the following ways:
- Add the assembly-based permission:
Open the _AssemblyInfo.cs_ file under the **Properties** folder and add:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.Vibrate)]
```
\- or -
- Update the Android Manifest:
Open the _AndroidManifest.xml_ file under the **Properties** folder and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.VIBRATE" />
```
<!-- TODO not yet supported
\- or -
- Use the Android project properties:
Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the **VIBRATE** permission. This will automatically update the _AndroidManifest.xml_ file.
-->
# [iOS](#tab/ios)
No setup is required.
# [Windows](#tab/windows)
No setup is required.
-----
<!-- markdownlint-enable MD025 -->
## Use haptic feedback
Haptic feedback is accessed through default implementation of the `IHapticFeedback` interface, available from the `Microsoft.Maui.Devices.HapticFeedback.Current` property. The haptic feedback functionality is performed in two modes: a short `Click` or a `LongPress`. The following code example initiates a `Click` or `LongPress` haptic feedback response to the user based on which `Button` they click:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="hapticfeedback":::

Просмотреть файл

@ -0,0 +1,79 @@
---
title: "Device information"
description: "Learn how to use the .NET MAUI DeviceInfo class in the Microsoft.Maui.Devices namespace, which provides information about the device the app is running on."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices"]
---
# Device information
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `DeviceInfo` class to read information about the device the app is running on.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `DeviceInfo` class is available in the `Microsoft.Maui.Devices` namespace.
## Read device info
Information about your device's is accessed by the default implementation of the `IDeviceInfo` interface, which is available by accessing the `Microsoft.Maui.Devices.DeviceInfo.Current` property. The `IDeviceInfo` interface provides many properties that describe the device, such as the manufacturer and idiom. The following example demonstrates reading the device info properties:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="read_info":::
## Get the device platform
The `IDeviceInfo.Platform` property represents the operating system the app is running on. The `DevicePlatform` type provides a property for each operating system:
- `DevicePlatform.Android`
- `DevicePlatform.iOS`
- `DevicePlatform.macOS`
- `DevicePlatform.MacCatalyst`
- `DevicePlatform.tvOS`
- `DevicePlatform.Tizen`
- `DevicePlatform.WinUI`
- `DevicePlatform.watchOS`
- `DevicePlatform.Unknown`
The following example demonstrates checking if the `DeviceInfo.Platform` property matches the `Android` operating system:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="check_for_android":::
## Get the device type
The `DeviceInfo.Idiom` property represents the type of device the app is running on, such as a desktop computer or a tablet. The `DeviceIdiom` type provides a property for each type of device:
- `DeviceIdiom.Phone`
- `DeviceIdiom.Tablet`
- `DeviceIdiom.Desktop`
- `DeviceIdiom.TV`
- `DeviceIdiom.Watch`
- `DeviceIdiom.Unknown`
The following example demonstrates comparing the `DeviceInfo.Idiom` value to a `DeviceIdiom` property:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="idiom":::
## Device type
`IDeviceInfo.DeviceType` property an enumeration to determine if the application is running on a physical or virtual device. A virtual device is a simulator or emulator.
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="device_type":::
## Platform differences
This section describes the platform-specific differences with the device information.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
No platform differences.
# [iOS](#tab/ios)
iOS doesn't expose an API for developers to get the model of the specific iOS device. Instead, a hardware identifier is returned, like _iPhone10,6_, which refers to the iPhone X. A mapping of these identifiers isn't provided by Apple, but can be found on the internet such as at [The iPhone Wiki](https://www.theiphonewiki.com/wiki/Models) and [Get iOS Model](https://github.com/dannycabrera/Get-iOS-Model) websites.
# [Windows](#tab/windows)
No platform differences.
-----
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,245 @@
---
title: Accessing device sensors overview
description: "Learn how to use and monitor sensors provided by your device, with .NET MAUI. You can monitor the following sensors: accelerometer, barometer, compass, shake, gyroscope, magnetometer, orientation."
ms.topic: overview
ms.date: 05/23/2022
ms.custom: template-overview
show_latex: true
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices", "Microsoft.Maui.Devices.Sensors"]
---
# Accessing device sensors
Devices have all sorts of sensors that are available to you. Some sensors can detect movement, others changes in the environment, such as light. Monitoring and reacting to these sensors makes your app dynamic in adapting to how the device is being used. You can also respond to changes in the sensors and alert the user. This article gives you a brief overview of the common sensors supported by .NET Multi-User Application (.NET MAUI).
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
Device sensor-related types are available in the `Microsoft.Maui.Devices.Sensors` namespace.
## Sensor speed
Sensor speed sets the speed in which a sensor will return data to your app. When you start a sensor, you provide the desired sensor speed with the `SensorSpeed` enumeration.
- `Fastest`\
Get the sensor data as fast as possible (not guaranteed to return on UI thread).
- `Game`\
Rate suitable for games (not guaranteed to return on UI thread).
- `Default`\
Default rate suitable for screen orientation changes.
- `UI`\
Rate suitable for general user interface.
> [!WARNING]
> Monitoring too many sensors at once may affect the rate sensor data is returned to your app.
### Sensor event handlers
Event handlers added to sensors with either the `Game` or `Fastest` speeds **aren't** guaranteed to run on the UI thread. If the event handler needs to access user-interface elements, use the [`MainThread.BeginInvokeOnMainThread`](../appmodel/main-thread.md) method to run that code on the UI thread.
## Accelerometer
The accelerometer sensor measures the acceleration of the device along its three axes. The data reported by the sensor represents how the user is moving the device.
To start monitoring the accelerometer sensor, call the `IAccelerometer.Start` method. .NET MAUI sends accelerometer data changes to your app by raising the `IAccelerometer.ReadingChanged` event. Use the `IAccelerometer.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the accelerometer with the `IAccelerometer.IsMonitoring` property, which will be `true` if the accelerometer was started and is currently being monitored.
The following code example demonstrates monitoring the accelerometer for changes. The `Accelerometer.Default` instance would be passed to the `ToggleAccelerometer` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_accelerometer":::
Accelerometer readings are reported back in **G**. A **G** is a unit of gravitation force equal to the gravity exerted by the earth's gravitational field $(9.81 m/s^2)$.
The coordinate-system is defined relative to the screen of the device in its default orientation. The axes aren't swapped when the device's screen orientation changes.
The **X** axis is horizontal and points to the right, the **Y** axis is vertical and points up and the **Z** axis points towards the outside of the front face of the screen. In this system, coordinates behind the screen have negative **Z** values.
Examples:
- When the device lies flat on a table and is pushed on its left side toward the right, the **X** acceleration value is positive.
- When the device lies flat on a table, the acceleration value is +1.00 G or $(+9.81 m/s^2)$, which correspond to the acceleration of the device $(0 m/s^2)$ minus the force of gravity $(-9.81 m/s^2)$ and normalized as in G.
<!-- TODO: Why are A and G mentioned here as if they're XYZ properties from the data? -->
- When the device lies flat on a table and is pushed toward the sky with an acceleration of **A** $m/s^2$, the acceleration value is equal to $A+9.81$ which corresponds to the acceleration of the device $(+A m/s^2)$ minus the force of gravity $(-9.81 m/s^2)$ and normalized in **G**.
### Platform-specific information (Accelerometer)
There is no platform-specific information related to the accelerometer sensor.
## Barometer
The barometer sensor measures the ambient air pressure. The data reported by the sensor represents the current air pressure. This data is reported the first time you start monitoring the sensor and then each time the pressure changes.
To start monitoring the barometer sensor, call the `IBarometer.Start` method. .NET MAUI sends air pressure readings to your app by raising the `IBarometer.ReadingChanged` event. Use the `IBarometer.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the barometer with the `IBarometer.IsMonitoring` property, which will be `true` if the barometer is currently being monitored.
The pressure reading is represented in hectopascals.
The following code example demonstrates monitoring the barometer for changes. The `Barometer.Default` instance would be passed to the `ToggleBarometer` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_barometer":::
### Platform-specific information (Barometer)
This section describes platform-specific implementation details related to the barometer sensor.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
No platform-specific implementation details.
# [iOS](#tab/ios)
This API uses [CMAltimeter](https://developer.apple.com/documentation/coremotion/cmaltimeter#//apple_ref/occ/cl/CMAltimeter) to monitor pressure changes, which is a hardware feature that was added to iPhone 6 and newer devices. A `FeatureNotSupportedException` will be thrown on devices that don't support the altimeter, the sensor used to report air pressure.
`SensorSpeed` isn't used by this API, as it isn't supported on iOS.
# [Windows](#tab/windows)
No platform-specific implementation details.
-----
<!-- markdownlint-enable MD025 -->
## Compass
The compass sensor monitors the device's magnetic north heading.
To start monitoring the compass sensor, call the `Compass.Start` method. .NET MAUI raises the `Compass.ReadingChanged` event when the compass heading changes. Use the `Compass.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the compass with the `Compass.IsMonitoring` property, which will be `true` if the compass is currently being monitored.
The following code example demonstrates monitoring the compass for changes. The `Compass.Default` instance would be passed to the `ToggleCompass` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_compass":::
### Platform-specific information (Compass)
This section describes platform-specific implementation details related to the compass feature.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
Android doesn't provide an API for retrieving the compass heading. .NET MAUI uses the accelerometer and magnetometer sensors to calculate the magnetic north heading, which is recommended by Google.
In rare instances, you maybe see inconsistent results because the sensors need to be calibrated. Recalibrating the compass on Android varies by phone model and Android version. You'll need to search the internet on how to recalibrate the compass. Here are two links that may help in recalibrating the compass:
- [Google Help Center: Find and improve your locations accuracy](https://support.google.com/maps/answer/2839911)
- [Stack Exchange Android Enthusiasts: How can I calibrate the compass on my phone?](https://android.stackexchange.com/questions/10145/how-can-i-calibrate-the-compass-on-my-phone)
Running multiple sensors from your app at the same time may impair the sensor speed.
### Lowpass filter
Because of how the Android compass values are updated and calculated, there may be a need to smooth out the values. A _Lowpass filter_ can be applied that averages the sine and cosine values of the angles and can be turned on by using the `Start` method overload, which accepts the `bool applyLowPassFilter` parameter:
```csharp
Compass.Default.Start(SensorSpeed.UI, applyLowPassFilter: true);
```
This is only applied on the Android platform, and the parameter is ignored on iOS and Windows. For more information, see [this GitHub issue comment](https://github.com/xamarin/Essentials/pull/354#issuecomment-405316860).
# [iOS](#tab/ios)
No platform-specific implementation details.
# [Windows](#tab/windows)
No platform-specific implementation details.
-----
<!-- markdownlint-enable MD025 -->
## Shake
Even though this article is listing **shake** as a sensor, it isn't. The [accelerometer](#accelerometer) is used to detect when the device is shaken.
The detect shake API uses raw readings from the accelerometer to calculate acceleration. It uses a simple queue mechanism to detect if 3/4ths of the recent accelerometer events occurred in the last half second. Acceleration is calculated by adding the square of the X, Y, and Z ($x^2+y^2+z^2$) readings from the accelerometer and comparing it to a specific threshold.
To start monitoring the accelerometer sensor, call the `IAccelerometer.Start` method. When a shake is detected, the `IAccelerometer.ShakeDetected` event is raised. Use the `IAccelerometer.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the accelerometer with the `IAccelerometer.IsMonitoring` property, which will be `true` if the accelerometer was started and is currently being monitored.
It's recommended to use `Game` or faster for the `SensorSpeed`.
The following code example demonstrates monitoring the accelerometer for the `ShakeDetected` event. The `Accelerometer.Default` instance would be passed to the `ToggleShake` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_shake":::
### Platform-specific information (Shake)
There is no platform-specific information related to the accelerometer sensor.
## Gyroscope
The gyroscope sensor measures the angular rotation speed around the device's three primary axes.
To start monitoring the gyroscope sensor, call the `IGyroscope.Start` method. .NET MAUI sends gyroscope data changes to your app by raising the `IGyroscope.ReadingChanged` event. The data provided by this event is measured in rad/s (radian per second). Use the `IGyroscope.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the gyroscope with the `IGyroscope.IsMonitoring` property, which will be `true` if the gyroscope was started and is currently being monitored.
The following code example demonstrates monitoring the gyroscope. The `Gyroscope.Default` instance would be passed to the `ToggleGyroscope` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_gyroscope":::
### Platform-specific information (Gyroscope)
There is no platform-specific information related to the gyroscope sensor.
## Magnetometer
The magnetometer sensor indicates the device's orientation relative to Earth's magnetic field.
To start monitoring the magnetometer sensor, call the `IMagnetometer.Start` method. .NET MAUI sends magnetometer data changes to your app by raising the `IMagnetometer.ReadingChanged` event. The data provided by this event is measured in $µT$ (microteslas). Use the `IMagnetometer.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the magnetometer with the `IMagnetometer.IsMonitoring` property, which will be `true` if the magnetometer was started and is currently being monitored.
The following code example demonstrates monitoring the magnetometer. The `Magnetometer.Default` instance would be passed to the `ToggleMagnetometer` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_magnetometer":::
### Platform-specific information (Magnetometer)
There is no platform-specific information related to the magnetometer sensor.
## Orientation
The orientation sensor monitors the orientation of a device in 3D space.
> [!NOTE]
> This sensor isn't used for determining if the device's video display is in portrait or landscape mode. Use the `Orientation` property of the `ScreenMetrics` object available from the [`DeviceDisplay`](../device/display.md) class.
To start monitoring the orientation sensor, call the `IOrientationSensor.Start` method. .NET MAUI sends orientation data changes to your app by raising the `IOrientationSensor.ReadingChanged` event. Use the `IOrientationSensor.Stop` method to stop monitoring the sensor. You can detect the monitoring state of the orientation with the `IOrientationSensor.IsMonitoring` property, which will be `true` if the orientation was started and is currently being monitored.
The following code example demonstrates monitoring the orientation sensor. The `OrientationSensor.Default` instance would be passed to the `ToggleOrientation` method:
:::code language="csharp" source="../snippets/shared_1/SensorsPage.xaml.cs" id="toggle_orientation":::
`IOrientationSensor` readings are reported back in the form of a [`Quaternion`](xref:System.Numerics.Quaternion) that describes the orientation of the device based on two 3D coordinate systems:
The device (generally a phone or tablet) has a 3D coordinate system with the following axes:
- The positive X-axis points to the right of the display in portrait mode.
- The positive Y-axis points to the top of the device in portrait mode.
- The positive Z-axis points out of the screen.
The 3D coordinate system of the Earth has the following axes:
- The positive X-axis is tangent to the surface of the Earth and points east.
- The positive Y-axis is also tangent to the surface of the Earth and points north.
- The positive Z-axis is perpendicular to the surface of the Earth and points up.
The `Quaternion` describes the rotation of the device's coordinate system relative to the Earth's coordinate system.
A `Quaternion` value is closely related to rotation around an axis. If an axis of rotation is the normalized vector ($a_x, a_y, a_z$), and the rotation angle is $\theta$, then the (X, Y, Z, W) components of the quaternion are:
$(a_x \times \sin(\theta/2), a_y \times \sin(\theta/2), a_z \times \sin(\theta/2), \cos(\theta/2)$
These are right-hand coordinate systems, so with the thumb of the right hand pointed in the positive direction of the rotation axis, the curve of the fingers indicate the direction of rotation for positive angles.
Examples:
- When the device lies flat on a table with its screen facing up, with the top of the device (in portrait mode) pointing north, the two coordinate systems are aligned. The `Quaternion` value represents the identity quaternion (0, 0, 0, 1). All rotations can be analyzed relative to this position.
- When the device lies flat on a table with its screen facing up, and the top of the device (in portrait mode) pointing west, the `Quaternion` value is (0, 0, 0.707, 0.707). The device has been rotated 90 degrees around the Z axis of the Earth.
- When the device is held upright so that the top (in portrait mode) points towards the sky, and the back of the device faces north, the device has been rotated 90 degrees around the X axis. The `Quaternion` value is (0.707, 0, 0, 0.707).
- If the device is positioned so its left edge is on a table, and the top points north, the device has been rotated -90 degrees around the Y axis (or 90 degrees around the negative Y axis). The `Quaternion` value is (0, -0.707, 0, 0.707).
### Platform-specific information (Orientation)
There is no platform-specific information related to the orientation sensor.

Просмотреть файл

@ -0,0 +1,89 @@
---
title: "Vibration"
description: "Learn how to use the .NET MAUI Vibration class, which lets you start and stop the vibrate functionality for a desired amount of time."
ms.date: 05/23/2022
no-loc: ["Microsoft.Maui", "Microsoft.Maui.Devices"]
---
# Vibration
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) `Vibration` class. This class lets you start and stop the vibrate functionality for a desired amount of time.
[!INCLUDE [docs under construction](~/includes/preview-note.md)]
The `HapticFeedback` class is available in the `Microsoft.Maui.Devices` namespace.
## Get started
To access the Vibration functionality, the following platform specific setup is required.
<!-- markdownlint-disable MD025 -->
# [Android](#tab/android)
The `VIBRATE` permission is required, and must be configured in the Android project. This can be added in the following ways:
- Add the assembly-based permission:
Open the _AssemblyInfo.cs_ file under the **Properties** folder and add:
```csharp
[assembly: UsesPermission(Android.Manifest.Permission.Vibrate)]
```
\- or -
- Update the Android Manifest:
Open the _AndroidManifest.xml_ file under the **Properties** folder and add the following in the `manifest` node:
```xml
<uses-permission android:name="android.permission.VIBRATE" />
```
<!-- TODO not yet supported
\- or -
- Use the Android project properties:
Right-click on the Android project and open the project's properties. Under _Android Manifest_ find the **Required permissions:** area and check the appropriate permissions. This will automatically update the _AndroidManifest.xml_ file.
-->
# [iOS](#tab/ios)
No additional setup required.
# [Windows](#tab/windows)
No additional setup required.
-----
## Vibrate the device
Haptic feedback is accessed through default implementation of the `IVibration` interface, available from the `Microsoft.Maui.Devices.Vibration.Default` property. The vibration functionality can be requested for a set amount of time or the default of 500 milliseconds. The following code example randomly vibrates the device between one and seven seconds:
:::code language="csharp" source="../snippets/shared_1/DeviceDetailsPage.xaml.cs" id="vibrate":::
## Platform differences
This section describes the platform-specific differences with the vibration API.
<!-- markdownlint-disable MD025 -->
<!-- markdownlint-disable MD024 -->
### [Android](#tab/android)
No platform differences.
# [iOS](#tab/ios)
- Only vibrates when device is set to "Vibrate on ring".
- Always vibrates for 500 milliseconds.
- Not possible to cancel vibration.
# [Windows](#tab/windows)
No platform differences.
-----
<!-- markdownlint-enable MD024 -->
<!-- markdownlint-enable MD025 -->

Просмотреть файл

@ -0,0 +1,93 @@
---
ms.topic: include
ms.date: 05/23/2022
---
When requesting a share or opening launcher on iPadOS, you can present it in a popover. This specifies where the popover will appear and point an arrow directly to. This location is often the control that launched the action. You can specify the location using the `PresentationSourceBounds` property:
```csharp
await Share.RequestAsync(new ShareFileRequest
{
Title = Title,
File = new ShareFile(file),
PresentationSourceBounds = DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Idiom == DeviceIdiom.Tablet
? new System.Drawing.Rectangle(0, 20, 0, 0)
: System.Drawing.Rectangle.Empty
});
```
```csharp
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(file),
PresentationSourceBounds = DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Idiom == DeviceIdiom.Tablet
? new System.Drawing.Rectangle(0, 20, 0, 0)
: System.Drawing.Rectangle.Empty
});
```
<!-- TODO: Is this stuff Apple specific? It seems generic. I know the previous section is because it references iOS, but that's done in this code -->
Everything described here works equally for `Share` and `Launcher`.
Here are some extension methods that help calculate the bounds of a view:
```csharp
public static class ViewHelpers
{
public static Rectangle GetAbsoluteBounds(this Microsoft.Maui.Controls.View element)
{
Element looper = element;
var absoluteX = element.X + element.Margin.Top;
var absoluteY = element.Y + element.Margin.Left;
// Add logic to handle titles, headers, or other non-view bars
while (looper.Parent != null)
{
looper = looper.Parent;
if (looper is Microsoft.Maui.Controls.View v)
{
absoluteX += v.X + v.Margin.Top;
absoluteY += v.Y + v.Margin.Left;
}
}
return new Rectangle(absoluteX, absoluteY, element.Width, element.Height);
}
}
```
This can then be used when calling `RequestAsync`:
```csharp
public Command<Microsoft.Maui.Controls.View> ShareCommand { get; } = new Command<Microsoft.Maui.Controls.View>(Share);
async void Share(Microsoft.Maui.Controls.View element)
{
try
{
await Share.Default.RequestAsync(new ShareTextRequest
{
PresentationSourceBounds = element.GetAbsoluteBounds(),
Title = "Title",
Text = "Text"
});
}
catch (Exception)
{
// Handle exception that share failed
}
}
```
You can pass in the calling element when the `Command` is triggered:
```xml
<Button Text="Share"
Command="{Binding ShareWithFriendsCommand}"
CommandParameter="{Binding Source={RelativeSource Self}}"/>
```
For an example of the `ViewHelpers` class, see the [.NET MAUI Sample hosted on GitHub](https://github.com/dotnet/maui/blob/main/src/Essentials/samples/Samples/Helpers/ViewHelpers.cs).

Просмотреть файл

@ -0,0 +1,7 @@
---
ms.topic: include
ms.date: 05/23/2022
---
> [!TIP]
> The `FullPath` property doesn't always return the physical path to the file. To get the file, use the `OpenReadAsync` method.

Просмотреть файл

@ -0,0 +1,9 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PlatformIntegration"
x:Class="PlatformIntegration.App">
<Application.Resources>
<ResourceDictionary Source="Resources/Styles.xaml" />
</Application.Resources>
</Application>

Просмотреть файл

@ -0,0 +1,32 @@
namespace PlatformIntegration;
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
//<appaction_handle>
public static void HandleAppActions(AppAction appAction)
{
App.Current.Dispatcher.Dispatch(async () =>
{
var page = appAction.Id switch
{
"battery_info" => new SensorsPage(),
"app_info" => new AppModelPage(),
_ => default(Page)
};
if (page != null)
{
await Application.Current.MainPage.Navigation.PopToRootAsync();
await Application.Current.MainPage.Navigation.PushAsync(page);
}
});
}
//</appaction_handle>
}

Просмотреть файл

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PlatformIntegration.AppModelPage"
Title="AppModelPage">
<StackLayout>
<HorizontalStackLayout>
<Label Text="AppInfo" VerticalOptions="Center" />
<Button Text="Read" Clicked="ReadAppInfoButton_Clicked" />
<Label x:Name="AppInfoLabel" VerticalOptions="Center" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Theme" VerticalOptions="Center" />
<Button Text="Read" Clicked="ReadThemeButton_Clicked" />
<Label x:Name="ThemeInfoLabel" VerticalOptions="Center" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Launcher" VerticalOptions="Center" />
<Button Text="Open LYFT" Clicked="OpenRideShareButton_Clicked" />
<Button Text="Open LYFT 2" Clicked="OpenRideShare2Button_Clicked" />
<Button Text="Open File" Clicked="OpenFileButton_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Maps" VerticalOptions="Center" />
<Button Text="Open MS Building" Clicked="Navigate1_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Browser" VerticalOptions="Center" />
<Button Text="Open" Clicked="BrowserOpen_Clicked" />
<Button Text="Open Customized" Clicked="BrowserCustomOpen_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Button Text="Read Version" Clicked="ReadVersion_Clicked" />
<VerticalStackLayout>
<Label x:Name="labelIsFirst" />
<Label x:Name="labelCurrentVersionIsFirst" />
<Label x:Name="labelCurrentBuildIsFirst" />
<Label x:Name="labelCurrentVersion" />
<Label x:Name="labelCurrentBuild" />
<Label x:Name="labelPreviousVersion" />
<Label x:Name="labelPreviousBuild" />
<Label x:Name="labelFirstInstalledVer" />
<Label x:Name="labelFirstInstalledBuild" />
<Label x:Name="labelVersionHistory" />
<Label x:Name="labelBuildHistory" />
</VerticalStackLayout>
</HorizontalStackLayout>
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,457 @@
namespace PlatformIntegration;
public partial class AppModelPage : ContentPage
{
public AppModelPage()
{
InitializeComponent();
}
private void ReadAppInfoButton_Clicked(object sender, EventArgs e)
{
//<read_info>
string name = AppInfo.Current.Name;
string package = AppInfo.Current.PackageName;
string version = AppInfo.Current.VersionString;
string build = AppInfo.Current.BuildString;
//</read_info>
//<show_settings>
AppInfo.Current.ShowSettingsUI();
//</show_settings>
}
private void ReadThemeButton_Clicked(object sender, EventArgs e)
{
//<read_theme>
ThemeInfoLabel.Text = AppInfo.Current.RequestedTheme switch
{
AppTheme.Dark => "Dark theme",
AppTheme.Light => "Light theme",
_ => "Unknown"
};
//</read_theme>
}
private async void OpenRideShareButton_Clicked(object sender, EventArgs e)
{
//<launcher_open>
bool supportsUri = await Launcher.Default.CanOpenAsync("lyft://");
if (supportsUri)
await Launcher.Default.OpenAsync("lyft://ridetype?id=lyft_line");
//</launcher_open>
}
private async void OpenRideShare2Button_Clicked(object sender, EventArgs e)
{
//<launcher_open_try>
bool launcherOpened = await Launcher.Default.TryOpenAsync("lyft://ridetype?id=lyft_line");
if (launcherOpened)
{
// Do something fun
}
//</launcher_open_try>
}
private async void OpenFileButton_Clicked(object sender, EventArgs e)
{
//<launcher_open_file>
string popoverTitle = "Read text file";
string name = "File.txt";
string file = System.IO.Path.Combine(FileSystem.CacheDirectory, name);
System.IO.File.WriteAllText(file, "Hello World");
await Launcher.Default.OpenAsync(new OpenFileRequest(popoverTitle, new ReadOnlyFile(file)));
//</launcher_open_file>
}
public void RunCode1()
{
//<runcode_lambda>
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
//</runcode_lambda>
}
public void RunCode2()
{
//<runcode_func_pointer>
void MyMainThreadCode()
{
// Code to run on the main thread
}
MainThread.BeginInvokeOnMainThread(MyMainThreadCode);
//</runcode_func_pointer>
}
public void RunCode3()
{
void MyMainThreadCode()
{
// Code to run on the main thread
}
//<runcode_test_thread>
if (MainThread.IsMainThread)
MyMainThreadCode();
else
MainThread.BeginInvokeOnMainThread(MyMainThreadCode);
//</runcode_test_thread>
}
private void Navigate1_Clicked(object sender, EventArgs e)
{
NavigateToBuilding25().Wait();
}
//<navigate_building>
public async Task NavigateToBuilding25()
{
var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };
try
{
await Map.Default.OpenAsync(location, options);
}
catch (Exception ex)
{
// No map application available to open
}
}
//</navigate_building>
//<navigate_building_placemark>
public async Task NavigateToBuilding()
{
var placemark = new Placemark
{
CountryName = "United States",
AdminArea = "WA",
Thoroughfare = "Microsoft Building 25",
Locality = "Redmond"
};
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };
try
{
await Map.Default.OpenAsync(placemark, options);
}
catch (Exception ex)
{
// No map application available to open or placemark can not be located
}
}
//</navigate_building_placemark>
//<navigate_building_placemark_extension>
public async Task NavigateToBuildingByPlacemark()
{
var placemark = new Placemark
{
CountryName = "United States",
AdminArea = "WA",
Thoroughfare = "Microsoft Building 25",
Locality = "Redmond"
};
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };
try
{
await placemark.OpenMapsAsync(options);
}
catch (Exception ex)
{
// No map application available to open or placemark can not be located
}
}
//</navigate_building_placemark_extension>
//<navigate_building_driving>
public async Task DriveToBuilding25()
{
var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { Name = "Microsoft Building 25",
NavigationMode = NavigationMode.Driving };
try
{
await Map.Default.OpenAsync(location, options);
}
catch (Exception ex)
{
// No map application available to open
}
}
//</navigate_building_driving>
//<browser_open>
private async void BrowserOpen_Clicked(object sender, EventArgs e)
{
try
{
Uri uri = new Uri("https://www.microsoft.com");
await Browser.Default.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
}
catch (Exception ex)
{
// An unexpected error occured. No browser may be installed on the device.
}
}
//</browser_open>
//<browser_open_custom>
private async void BrowserCustomOpen_Clicked(object sender, EventArgs e)
{
try
{
Uri uri = new Uri("https://www.microsoft.com");
BrowserLaunchOptions options = new BrowserLaunchOptions()
{
LaunchMode = BrowserLaunchMode.SystemPreferred,
TitleMode = BrowserTitleMode.Show,
PreferredToolbarColor = Colors.Violet,
PreferredControlColor = Colors.SandyBrown
};
await Browser.Default.OpenAsync(uri, options);
}
catch (Exception ex)
{
// An unexpected error occured. No browser may be installed on the device.
}
}
//</browser_open_custom>
private async void CheckLocationPermission()
{
try
{
//<permission_check>
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
//</permission_check>
switch (status)
{
case PermissionStatus.Unknown:
break;
case PermissionStatus.Denied:
break;
case PermissionStatus.Disabled:
break;
case PermissionStatus.Granted:
break;
case PermissionStatus.Restricted:
break;
case PermissionStatus.Limited:
break;
default:
break;
}
}
catch (PermissionException ex)
{
// Permission not declared
}
}
private async void RequestLocationPermission()
{
try
{
//<permission_request>
PermissionStatus status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
//</permission_request>
switch (status)
{
case PermissionStatus.Unknown:
break;
case PermissionStatus.Denied:
break;
case PermissionStatus.Disabled:
break;
case PermissionStatus.Granted:
break;
case PermissionStatus.Restricted:
break;
case PermissionStatus.Limited:
break;
default:
break;
}
}
catch (PermissionException ex)
{
// Permission not declared
}
}
//<permission_check_and_request>
public async Task<PermissionStatus> CheckAndRequestLocationPermission()
{
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
if (status == PermissionStatus.Granted)
return status;
if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
{
// Prompt the user to turn on in settings
// On iOS once a permission has been denied it may not be requested again from the application
return status;
}
if (Permissions.ShouldShowRationale<Permissions.LocationWhenInUse>())
{
// Prompt the user with additional information as to why the permission is needed
}
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
return status;
}
//</permission_check_and_request>
// The following code was removed from the article. I felt like it confused the previous code example without
// adding anything much to the concept.
//<permission_check_and_request_usage>
public async Task<Location> GetLocationAsync()
{
PermissionStatus status = await CheckAndRequestPermissionAsync(new Permissions.LocationWhenInUse());
if (status != PermissionStatus.Granted)
{
// Notify user permission was denied
return null;
}
return await Geolocation.GetLocationAsync();
}
public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>(T permission)
where T : Permissions.BasePermission
{
PermissionStatus status = await permission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
status = await permission.RequestAsync();
return status;
}
//<permission_check_and_request_usage>
//<permission_class>
public class MyPermission : Permissions.BasePermission
{
// This method checks if current status of the permission.
public override Task<PermissionStatus> CheckStatusAsync()
{
throw new System.NotImplementedException();
}
// This method is optional and a PermissionException is often thrown if a permission is not declared.
public override void EnsureDeclared()
{
throw new System.NotImplementedException();
}
// Requests the user to accept or deny a permission.
public override Task<PermissionStatus> RequestAsync()
{
throw new System.NotImplementedException();
}
// Indicates that the requestor should prompt the user as to why the app requires the permission, because the
// user has previously denied this permission.
public override bool ShouldShowRationale()
{
throw new NotImplementedException();
}
}
//</permission_class>
#if ANDROID
//<permission_readwrite>
public class ReadWriteStoragePerms : Permissions.BasePlatformPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new List<(string androidPermission, bool isRuntime)>
{
(global::Android.Manifest.Permission.ReadExternalStorage, true),
(global::Android.Manifest.Permission.WriteExternalStorage, true)
}.ToArray();
}
//</permission_readwrite>
#elif WINDOWS
public class ReadWriteStoragePerms : Permissions.BasePlatformPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new List<(string androidPermission, bool isRuntime)>
{
//(global::Android.Manifest.Permission.ReadExternalStorage, true),
//(global::Android.Manifest.Permission.WriteExternalStorage, true)
}.ToArray();
}
#endif
private async void RequestCustomPermission()
{
try
{
//<permission_readwrite_request>
PermissionStatus status = await Permissions.RequestAsync<ReadWriteStoragePerms>();
//</permission_readwrite_request>
switch (status)
{
case PermissionStatus.Unknown:
break;
case PermissionStatus.Denied:
break;
case PermissionStatus.Disabled:
break;
case PermissionStatus.Granted:
break;
case PermissionStatus.Restricted:
break;
case PermissionStatus.Limited:
break;
default:
break;
}
}
catch (PermissionException ex)
{
// Permission not declared
}
}
//<version_read>
private void ReadVersion_Clicked(object sender, EventArgs e)
{
labelIsFirst.Text = VersionTracking.IsFirstLaunchEver.ToString();
labelCurrentVersionIsFirst.Text = VersionTracking.IsFirstLaunchForCurrentVersion.ToString();
labelCurrentBuildIsFirst.Text = VersionTracking.IsFirstLaunchForCurrentBuild.ToString();
labelCurrentVersion.Text = VersionTracking.CurrentVersion.ToString();
labelCurrentBuild.Text = VersionTracking.CurrentBuild.ToString();
labelFirstInstalledVer.Text = VersionTracking.FirstInstalledVersion.ToString();
labelFirstInstalledBuild.Text = VersionTracking.FirstInstalledBuild.ToString();
labelVersionHistory.Text = String.Join(',', VersionTracking.VersionHistory);
labelBuildHistory.Text = String.Join(',', VersionTracking.BuildHistory);
// These two properties may be null if this is the first version
labelPreviousVersion.Text = VersionTracking.PreviousVersion?.ToString() ?? "none";
labelPreviousBuild.Text = VersionTracking.PreviousBuild?.ToString() ?? "none";
}
//</version_read>
}

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="PlatformIntegration.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PlatformIntegration"
Shell.FlyoutBehavior="Disabled">
<ShellContent
Title="Hello, World!"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />
</Shell>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegration;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PlatformIntegration.BatteryTestPage"
Title="BatteryTestPage">
<StackLayout>
<HorizontalStackLayout>
<Label Text="Battery" VerticalOptions="Center" />
<Switch Toggled="BatterySwitch_Toggled" VerticalOptions="Center" />
<VerticalStackLayout>
<Label x:Name="BatteryStateLabel" VerticalOptions="Center" LineBreakMode="CharacterWrap" />
<Label x:Name="BatteryLevelLabel" VerticalOptions="Center" LineBreakMode="CharacterWrap" />
</VerticalStackLayout>
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Battery Saver Monitor" VerticalOptions="Center" />
<Switch Toggled="BatterySaverSwitch_Toggled" VerticalOptions="Center" />
<Label x:Name="BatterySaverLabel" VerticalOptions="Center" LineBreakMode="CharacterWrap" TextColor="Green" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Button Text="Update battery source" Clicked="BatSourceButton_Clicked" />
<Label x:Name="BatteryPowerSourceLabel" VerticalOptions="Center" LineBreakMode="CharacterWrap" TextColor="Green" />
</HorizontalStackLayout>
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,89 @@
namespace PlatformIntegration;
public partial class BatteryTestPage : ContentPage
{
public BatteryTestPage()
{
InitializeComponent();
}
//<watch_battery>
private void BatterySwitch_Toggled(object sender, ToggledEventArgs e) =>
WatchBattery(Battery.Default);
private bool _isBatteryWatched;
private void WatchBattery(IBattery battery)
{
if (!_isBatteryWatched)
{
battery.BatteryInfoChanged += Battery_BatteryInfoChanged;
}
else
{
battery.BatteryInfoChanged -= Battery_BatteryInfoChanged;
}
_isBatteryWatched = !_isBatteryWatched;
}
private void Battery_BatteryInfoChanged(object sender, BatteryInfoChangedEventArgs e)
{
BatteryStateLabel.Text = e.State switch
{
BatteryState.Charging => "Battery is currently charging",
BatteryState.Discharging => "Charger is not connected and the battery is discharging",
BatteryState.Full => "Battery is full",
BatteryState.NotCharging => "The battery isn't charging.",
BatteryState.NotPresent => "Battery is not available.",
BatteryState.Unknown => "Battery is unknown",
_ => "Battery is unknown"
};
BatteryLevelLabel.Text = $"Battery is {e.ChargeLevel * 100}% charged.";
}
//</watch_battery>
//<energy_saver>
private bool _isBatteryLow = false;
private void BatterySaverSwitch_Toggled(object sender, ToggledEventArgs e)
{
// Capture the initial state of the battery
_isBatteryLow = Battery.Default.EnergySaverStatus == EnergySaverStatus.On;
BatterySaverLabel.Text = _isBatteryLow.ToString();
// Watch for any changes to the battery saver mode
Battery.Default.EnergySaverStatusChanged += Battery_EnergySaverStatusChanged;
}
private void Battery_EnergySaverStatusChanged(object sender, EnergySaverStatusChangedEventArgs e)
{
// Update the variable based on the state
_isBatteryLow = Battery.Default.EnergySaverStatus == EnergySaverStatus.On;
BatterySaverLabel.Text = _isBatteryLow.ToString();
}
//</energy_saver>
private void BatSourceButton_Clicked(object sender, EventArgs e)
{
SetChargeModeLabel();
}
//<charge_mode>
private void SetChargeModeLabel()
{
BatteryPowerSourceLabel.Text = Battery.Default.PowerSource switch
{
BatteryPowerSource.Wireless => "Wireless charging",
BatteryPowerSource.Usb => "USB cable charging",
BatteryPowerSource.AC => "Device is plugged in to a power source",
BatteryPowerSource.Battery => "Device isn't charging",
_ => "Unknown"
};
}
//</charge_mode>
}

Просмотреть файл

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PlatformIntegration.CommsPage"
Title="CommsPage">
<StackLayout>
<HorizontalStackLayout>
<Button Text="SelectContact" Clicked="SelectContactButton_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Button Text="OpenEmail" Clicked="SendEmailButton_Clicked" />
<Button Text="OpenEmailAttach" Clicked="SendEmailPictureButton_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Button Text="OpenSMS" Clicked="SendSmsButton_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Button Text="OpenPhone" Clicked="DialPhoneButton_Clicked" />
</HorizontalStackLayout>
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,123 @@
namespace PlatformIntegration;
public partial class CommsPage : ContentPage
{
public CommsPage()
{
InitializeComponent();
}
//<contact_select>
private async void SelectContactButton_Clicked(object sender, EventArgs e)
{
try
{
var contact = await Contacts.Default.PickContactAsync();
if (contact == null)
return;
string id = contact.Id;
string namePrefix = contact.NamePrefix;
string givenName = contact.GivenName;
string middleName = contact.MiddleName;
string familyName = contact.FamilyName;
string nameSuffix = contact.NameSuffix;
string displayName = contact.DisplayName;
List<ContactPhone> phones = contact.Phones; // List of phone numbers
List<ContactEmail> emails = contact.Emails; // List of email addresses
}
catch (Exception ex)
{
// Most likely permission denied
}
}
//</contact_select>
//<contact_all>
public async IAsyncEnumerable<string> GetContactNames()
{
var contacts = await Contacts.GetAllAsync();
// No contacts
if (contacts == null)
yield break;
foreach (var contact in contacts)
yield return contact.DisplayName;
}
//</contact_all>
private async void SendEmailButton_Clicked(object sender, EventArgs e)
{
//<email_compose>
if (Email.Default.IsComposeSupported)
{
string subject = "Hello friends!";
string body = "It was great to see you last weekend.";
string[] recipients = new[] { "john@contoso.com", "jane@contoso.com" };
var message = new EmailMessage
{
Subject = subject,
Body = body,
BodyFormat = EmailBodyFormat.PlainText,
To = new List<string>(recipients)
};
await Email.Default.ComposeAsync(message);
}
//</email_compose>
}
private async void SendEmailPictureButton_Clicked(object sender, EventArgs e)
{
//<email_picture>
if (Email.Default.IsComposeSupported)
{
string subject = "Hello friends!";
string body = "It was great to see you last weekend. I've attached a photo of our adventures together.";
string[] recipients = new[] { "john@contoso.com", "jane@contoso.com" };
var message = new EmailMessage
{
Subject = subject,
Body = body,
BodyFormat = EmailBodyFormat.PlainText,
To = new List<string>(recipients)
};
string picturePath = Path.Combine(FileSystem.CacheDirectory, "memories.jpg");
message.Attachments.Add(new EmailAttachment(picturePath));
await Email.Default.ComposeAsync(message);
}
//</email_picture>
}
private async void SendSmsButton_Clicked(object sender, EventArgs e)
{
//<sms_send>
if (Sms.Default.IsComposeSupported)
{
string[] recipients = new[] { "000-000-0000" };
string text = "Hello, I'm interested in buying your vase.";
var message = new SmsMessage(text, recipients);
await Sms.Default.ComposeAsync(message);
}
//</sms_send>
}
private void DialPhoneButton_Clicked(object sender, EventArgs e)
{
//<phone_dial>
if (PhoneDialer.Default.IsSupported)
PhoneDialer.Default.Open("000-000-0000");
//</phone_dial>
}
}

Просмотреть файл

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PlatformIntegration.DataPage"
Title="DataPage" Loaded="ContentPage_Loaded">
<StackLayout>
<HorizontalStackLayout>
<Label Text="Clipboard" VerticalOptions="Center" />
<Button Text="Set" Clicked="SetClipboardButton_Clicked" />
<Button Text="Read" Clicked="ReadClipboardButton_Clicked" />
<Label x:Name="ClipboardOutputLabel" VerticalOptions="Center" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Button Text="Share" Clicked="ShareButton_Clicked" />
</HorizontalStackLayout>
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,104 @@
namespace PlatformIntegration;
public partial class DataPage : ContentPage
{
public DataPage()
{
InitializeComponent();
}
//<clipboard_set>
private async void SetClipboardButton_Clicked(object sender, EventArgs e) =>
await Clipboard.Default.SetTextAsync("This text was highlighted in the UI.");
//</clipboard_set>
//<clipboard_read>
private async void ReadClipboardButton_Clicked(object sender, EventArgs e)
{
if (Clipboard.Default.HasText)
{
ClipboardOutputLabel.Text = await Clipboard.Default.GetTextAsync();
await ClearClipboard();
}
else
ClipboardOutputLabel.Text = "Clipboard is empty";
}
//<clipboard_clear>
private async Task ClearClipboard() =>
await Clipboard.Default.SetTextAsync(null);
//</clipboard_clear>
//</clipboard_read>
//<clipboard_event>
private void ContentPage_Loaded(object sender, EventArgs e)
{
Clipboard.Default.ClipboardContentChanged += Clipboard_ClipboardContentChanged;
}
private async void Clipboard_ClipboardContentChanged(object sender, EventArgs e)
{
ClipboardOutputLabel.Text = await Clipboard.Default.GetTextAsync();
}
//</clipboard_event>
private async void ShareButton_Clicked(object sender, EventArgs e)
{
await ShareText("Hello, welcome to the show.", Share.Default);
await ShareUri("http://www.microsoft.com", Share.Default);
await ShareFile(Share.Default);
await ShareMultipleFiles(Share.Default);
}
//<share_text_uri>
public async Task ShareText(string text, IShare share)
{
await share.RequestAsync(new ShareTextRequest
{
Text = text,
Title = "Share Text"
});
}
public async Task ShareUri(string uri, IShare share)
{
await share.RequestAsync(new ShareTextRequest
{
Uri = uri,
Title = "Share Web Link"
});
}
//</share_text_uri>
//<share_file>
public async Task ShareFile(IShare share)
{
string fn = "Attachment.txt";
string file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, "Hello World");
await share.RequestAsync(new ShareFileRequest
{
Title = "Share text file",
File = new ShareFile(file)
});
}
//</share_file>
//<share_file_multiple>
public async Task ShareMultipleFiles(IShare share)
{
string file1 = Path.Combine(FileSystem.CacheDirectory, "Attachment1.txt");
string file2 = Path.Combine(FileSystem.CacheDirectory, "Attachment2.txt");
File.WriteAllText(file1, "Content 1");
File.WriteAllText(file2, "Content 2");
await share.RequestAsync(new ShareMultipleFilesRequest
{
Title = "Share multiple files",
Files = new List<ShareFile> { new ShareFile(file1), new ShareFile(file2) }
});
}
//</share_file_multiple>
}

Просмотреть файл

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PlatformIntegration.DeviceDetailsPage"
Title="DeviceDetailsPage" Loaded="ContentPage_Loaded">
<StackLayout>
<HorizontalStackLayout>
<Label Text="Display Orientation" VerticalOptions="Center" />
<Label x:Name="DisplayDetailsLabel" VerticalOptions="Center" LineBreakMode="CharacterWrap" TextColor="Green" />
<Label x:Name="DisplayDeviceLabel" VerticalOptions="Center" LineBreakMode="CharacterWrap" TextColor="Green" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Display always on? " VerticalOptions="Center" />
<Switch x:Name="AlwaysOnSwitch" Toggled="AlwaysOnSwitch_Toggled" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Flashlight" VerticalOptions="Center" />
<Switch x:Name="FlashlightSwitch" Toggled="FlashlightSwitch_Toggled" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Haptic" VerticalOptions="Center" />
<Button Text="Click" Clicked="HapticShortButton_Clicked" />
<Button Text="Long" Clicked="HapticLongButton_Clicked" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Vibration" VerticalOptions="Center" />
<Button Text="Start" Clicked="VibrateStartButton_Clicked" />
<Button Text="Force stop" Clicked="VibrateStopButton_Clicked" />
</HorizontalStackLayout>
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,128 @@
namespace PlatformIntegration;
public partial class DeviceDetailsPage : ContentPage
{
public DeviceDetailsPage()
{
InitializeComponent();
}
private void ContentPage_Loaded(object sender, EventArgs e)
{
ReadDeviceDisplay();
ReadDeviceInfo();
}
//<main_display>
private void ReadDeviceDisplay()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine($"Pixel width: {DeviceDisplay.Current.MainDisplayInfo.Width} / Pixel Height: {DeviceDisplay.Current.MainDisplayInfo.Height}");
sb.AppendLine($"Density: {DeviceDisplay.Current.MainDisplayInfo.Density}");
sb.AppendLine($"Orientation: {DeviceDisplay.Current.MainDisplayInfo.Orientation}");
sb.AppendLine($"Rotation: {DeviceDisplay.Current.MainDisplayInfo.Rotation}");
sb.AppendLine($"Refresh Rate: {DeviceDisplay.Current.MainDisplayInfo.RefreshRate}");
DisplayDetailsLabel.Text = sb.ToString();
}
//</main_display>
//<read_info>
private void ReadDeviceInfo()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine($"Model: {DeviceInfo.Current.Model}");
sb.AppendLine($"Manufacturer: {DeviceInfo.Current.Manufacturer}");
sb.AppendLine($"Name: {DeviceInfo.Name}");
sb.AppendLine($"OS Version: {DeviceInfo.VersionString}");
sb.AppendLine($"Refresh Rate: {DeviceInfo.Current}");
sb.AppendLine($"Idiom: {DeviceInfo.Current.Idiom}");
sb.AppendLine($"Platform: {DeviceInfo.Current.Platform}");
//<device_type>
bool isVirtual = DeviceInfo.Current.DeviceType switch
{
DeviceType.Physical => false,
DeviceType.Virtual => true,
_ => false
};
//</device_type>
sb.AppendLine($"Virtual device? {isVirtual}");
DisplayDeviceLabel.Text = sb.ToString();
}
//</read_info>
//<check_for_android>
private bool IsAndroid() =>
DeviceInfo.Current.Platform == DevicePlatform.Android;
//</check_for_android>
//<always_on>
private void AlwaysOnSwitch_Toggled(object sender, ToggledEventArgs e) =>
DeviceDisplay.Current.KeepScreenOn = AlwaysOnSwitch.IsToggled;
//</always_on>
//<idiom>
private void PrintIdiom()
{
if (DeviceInfo.Current.Idiom == DeviceIdiom.Desktop)
Console.WriteLine("The current device is a desktop");
else if (DeviceInfo.Current.Idiom == DeviceIdiom.Phone)
Console.WriteLine("The current device is a phone");
else if (DeviceInfo.Current.Idiom == DeviceIdiom.Tablet)
Console.WriteLine("The current device is a Tablet");
}
//</idiom>
//<flashlight>
private async void FlashlightSwitch_Toggled(object sender, ToggledEventArgs e)
{
try
{
if (FlashlightSwitch.IsToggled)
await Flashlight.Default.TurnOnAsync();
else
await Flashlight.Default.TurnOffAsync();
}
catch (FeatureNotSupportedException ex)
{
// Handle not supported on device exception
}
catch (PermissionException ex)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to turn on/off flashlight
}
}
//</flashlight>
//<hapticfeedback>
private void HapticShortButton_Clicked(object sender, EventArgs e) =>
HapticFeedback.Current.Perform(HapticFeedbackType.Click);
private void HapticLongButton_Clicked(object sender, EventArgs e) =>
HapticFeedback.Current.Perform(HapticFeedbackType.LongPress);
//</hapticfeedback>
//<vibrate>
private void VibrateStartButton_Clicked(object sender, EventArgs e)
{
int secondsToVibrate = Random.Shared.Next(1, 7);
TimeSpan vibrationLength = TimeSpan.FromSeconds(secondsToVibrate);
Vibration.Default.Vibrate(vibrationLength);
}
private void VibrateStopButton_Clicked(object sender, EventArgs e) =>
Vibration.Default.Cancel();
//</vibrate>
}

Просмотреть файл

@ -0,0 +1,24 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class BrowserTest
{
public async Task OpenBrowser(Uri uri)
{
try
{
await Browser.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
}
catch (Exception ex)
{
// An unexpected error occured. No browser may be installed on the device.
}
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class ClipboardTest
{
public ClipboardTest()
{
// Register for clipboard changes, be sure to unsubscribe when needed
Clipboard.ClipboardContentChanged += OnClipboardContentChanged;
}
void OnClipboardContentChanged(object sender, EventArgs e)
{
Console.WriteLine($"Last clipboard change at {DateTime.UtcNow:T}");
}
public async void SetText()
{
await Clipboard.SetTextAsync("Hello World");
}
public async void GetText()
{
string text = await Clipboard.GetTextAsync();
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class ConnectivityTest
{
public ConnectivityTest()
{
// Register for connectivity changes, be sure to unsubscribe when finished
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
if (e.NetworkAccess == NetworkAccess.ConstrainedInternet)
Console.WriteLine("Internet access is available but is limited.");
else if (e.NetworkAccess != NetworkAccess.Internet)
Console.WriteLine("Internet access has been lost.");
// Log each active connection
Console.Write("Connections active: ");
foreach (var item in e.ConnectionProfiles)
{
switch (item)
{
case ConnectionProfile.Bluetooth:
Console.Write("Bluetooth");
break;
case ConnectionProfile.Cellular:
Console.Write("Cell");
break;
case ConnectionProfile.Ethernet:
Console.Write("Ethernet");
break;
case ConnectionProfile.WiFi:
Console.Write("WiFi");
break;
default:
break;
}
}
Console.WriteLine();
}
}
}

Просмотреть файл

@ -0,0 +1,68 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class ContactsTest
{
public ContactsTest()
{
}
public async void PickContact()
{
try
{
// TODO: Wont compile
//var contact = await Contacts.PickContactAsync();
//if (contact == null)
// return;
//var id = contact.Id;
//var namePrefix = contact.NamePrefix;
//var givenName = contact.GivenName;
//var middleName = contact.MiddleName;
//var familyName = contact.FamilyName;
//var nameSuffix = contact.NameSuffix;
//var displayName = contact.DisplayName;
//var phones = contact.Phones; // List of phone numbers
//var emails = contact.Emails; // List of email addresses
}
catch (Exception ex)
{
// Handle exception here.
}
}
public async void GetAllContacts()
{
ObservableCollection<Contact> contactsCollect = new ObservableCollection<Contact>();
try
{
// TODO: Wont compile
//// cancellationToken parameter is optional
//var cancellationToken = default(CancellationToken);
//var contacts = await Contacts.GetAllAsync(cancellationToken);
//if (contacts == null)
// return;
//foreach (var contact in contacts)
// contactsCollect.Add(contact);
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class DeviceInfoTest
{
public void ReadInfo()
{
// Device Model (SMG-950U, iPhone10,6)
string device = DeviceInfo.Model;
// Manufacturer (Samsung)
string manufacturer = DeviceInfo.Manufacturer;
// Device Name (Motz's iPhone)
string deviceName = DeviceInfo.Name;
// Operating System Version Number (7.0)
string version = DeviceInfo.VersionString;
// Platform (Android)
DevicePlatform platform = DeviceInfo.Platform;
// Idiom (Phone)
DeviceIdiom idiom = DeviceInfo.Idiom;
// Device Type (Physical)
DeviceType deviceType = DeviceInfo.DeviceType;
}
public void WritePlatform()
{
if (DeviceInfo.Platform == DevicePlatform.Android)
Console.WriteLine("The current OS is Android");
else if (DeviceInfo.Platform == DevicePlatform.iOS)
Console.WriteLine("The current OS is iOS");
else if (DeviceInfo.Platform == DevicePlatform.WinUI)
Console.WriteLine("The current OS is Windows");
else if (DeviceInfo.Platform == DevicePlatform.Tizen)
Console.WriteLine("The current OS is Tizen");
}
public void WriteIdiom()
{
if (DeviceInfo.Idiom == DeviceIdiom.Desktop)
Console.WriteLine("The current device is a desktop");
else if (DeviceInfo.Idiom == DeviceIdiom.Phone)
Console.WriteLine("The current device is a phone");
else if (DeviceInfo.Idiom == DeviceIdiom.Tablet)
Console.WriteLine("The current device is a Tablet");
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public partial class DisplayInfoTest
{
public DisplayInfoTest()
{
// Subscribe to changes of screen metrics
DeviceDisplay.MainDisplayInfoChanged += OnMainDisplayInfoChanged;
}
void OnMainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
// Process changes
DisplayInfo displayInfo = e.DisplayInfo;
}
}
public partial class DisplayInfoTest
{
public void ReadInfo()
{
// Get Metrics
DisplayInfo mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
// Orientation (Landscape, Portrait, Square, Unknown)
DisplayOrientation orientation = mainDisplayInfo.Orientation;
// Rotation (0, 90, 180, 270)
DisplayRotation rotation = mainDisplayInfo.Rotation;
// Width (in pixels)
double width = mainDisplayInfo.Width;
// Height (in pixels)
double height = mainDisplayInfo.Height;
// Screen density
double density = mainDisplayInfo.Density;
}
public void ToggleScreenLock()
{
DeviceDisplay.KeepScreenOn = !DeviceDisplay.KeepScreenOn;
}
}
}

Просмотреть файл

@ -0,0 +1,57 @@
using Microsoft.Maui.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace PlatformIntegration.Features
{
class FilePickerTest
{
async Task<FileResult> PickAndShow(PickOptions options)
{
try
{
var result = await FilePicker.PickAsync(options);
if (result != null)
{
if (result.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
result.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))
{
using var stream = await result.OpenReadAsync();
var image = ImageSource.FromStream(() => stream);
}
}
return result;
}
catch (Exception ex)
{
// The user canceled or something went wrong
}
return null;
}
public void DoStuff()
{
var customFileType = new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "public.my.comic.extension" } }, // or general UTType values
{ DevicePlatform.Android, new[] { "application/comics" } },
{ DevicePlatform.WinUI, new[] { ".cbr", ".cbz" } },
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "cbr", "cbz" } }, // or general UTType values
});
PickOptions options = new()
{
PickerTitle = "Please select a comic file",
FileTypes = customFileType,
};
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
using System.IO;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
class FileHelpersTest
{
public async Task<string> ReadTextFile(string filePath)
{
using Stream fileStream = await FileSystem.OpenAppPackageFileAsync(filePath);
using StreamReader reader = new StreamReader(fileStream);
return await reader.ReadToEndAsync();
}
public void GetFiles()
{
string cacheDir = FileSystem.CacheDirectory;
string mainDir = FileSystem.AppDataDirectory;
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
using System;
using System.IO;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
class FlashlightTest
{
public async Task RunFlashlight()
{
try
{
// Turn On
await Flashlight.TurnOnAsync();
// Pause for 3 seconds
await Task.Delay(TimeSpan.FromSeconds(3));
// Turn Off
await Flashlight.TurnOffAsync();
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to turn on/off flashlight
}
}
}
}

Просмотреть файл

@ -0,0 +1,71 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
class GeocodingTest
{
public async Task TranslateLocation()
{
try
{
var address = "Microsoft Building 25 Redmond WA USA";
var locations = await Geocoding.GetLocationsAsync(address);
var location = locations?.FirstOrDefault();
if (location != null)
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Handle exception that may have occurred in geocoding
}
}
public async Task TranslateCoordinates()
{
try
{
var lat = 47.673988;
var lon = -122.121513;
var placemarks = await Geocoding.GetPlacemarksAsync(lat, lon);
var placemark = placemarks?.FirstOrDefault();
if (placemark != null)
{
var geocodeAddress =
$"AdminArea: {placemark.AdminArea}\n" +
$"CountryCode: {placemark.CountryCode}\n" +
$"CountryName: {placemark.CountryName}\n" +
$"FeatureName: {placemark.FeatureName}\n" +
$"Locality: {placemark.Locality}\n" +
$"PostalCode: {placemark.PostalCode}\n" +
$"SubAdminArea: {placemark.SubAdminArea}\n" +
$"SubLocality: {placemark.SubLocality}\n" +
$"SubThoroughfare: {placemark.SubThoroughfare}\n" +
$"Thoroughfare: {placemark.Thoroughfare}\n";
Console.WriteLine(geocodeAddress);
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Handle exception that may have occurred in geocoding
}
}
}
}

Просмотреть файл

@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
class GeolocationTest
{
public async Task GetCachedLocation()
{
try
{
Location location = await Geolocation.GetLastKnownLocationAsync();
if (location != null)
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to get location
}
}
private CancellationTokenSource _cancelTokenSource;
private bool _isCheckingLocation;
public async Task GetCurrentLocation()
{
try
{
_isCheckingLocation = true;
var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
_cancelTokenSource = new CancellationTokenSource();
Location location = await Geolocation.GetLocationAsync(request, _cancelTokenSource.Token);
if (location != null)
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to get location
}
finally
{
_isCheckingLocation = false;
}
}
public void CancelRequest()
{
if (_cancelTokenSource != null && _cancelTokenSource.IsCancellationRequested == false)
_cancelTokenSource.Cancel();
}
public async Task CheckMock()
{
var request = new GeolocationRequest(GeolocationAccuracy.Medium);
Location location = await Geolocation.GetLocationAsync(request);
if (location != null && location.IsFromMockProvider)
{
// location is from a mock provider
}
}
public void Distance()
{
Location boston = new Location(42.358056, -71.063611);
Location sanFrancisco = new Location(37.783333, -122.416667);
double miles = Location.CalculateDistance(boston, sanFrancisco, DistanceUnits.Miles);
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class HapticTest
{
public void TriggerHapticFeedback()
{
try
{
// Perform click feedback
HapticFeedback.Perform(HapticFeedbackType.Click);
// Or use long press
HapticFeedback.Perform(HapticFeedbackType.LongPress);
}
catch (FeatureNotSupportedException ex)
{
// Feature not supported on device
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class LauncherTest
{
public async Task OpenRideShareAsync()
{
var supportsUri = await Launcher.CanOpenAsync("lyft://");
if (supportsUri)
await Launcher.OpenAsync("lyft://ridetype?id=lyft_line");
}
public async Task<bool> TryOpenRideShareAsync() =>
await Launcher.TryOpenAsync("lyft://ridetype?id=lyft_line");
public async Task OpenTextFile()
{
string popoverTitle = "Read text file";
string name = "File.txt";
string file = System.IO.Path.Combine(FileSystem.CacheDirectory, name);
System.IO.File.WriteAllText(file, "Hello World");
await Launcher.OpenAsync(new OpenFileRequest(popoverTitle, new ReadOnlyFile(file)));
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class MapTest
{
public async Task NavigateToBuilding25()
{
var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };
try
{
await Map.OpenAsync(location, options);
}
catch (Exception ex)
{
// No map application available to open
}
}
}
public class MapTest2
{
public async Task NavigateToBuilding25()
{
var placemark = new Placemark
{
CountryName = "United States",
AdminArea = "WA",
Thoroughfare = "Microsoft Building 25",
Locality = "Redmond"
};
var options = new MapLaunchOptions { Name = "Microsoft Building 25" };
try
{
await Map.OpenAsync(placemark, options);
}
catch (Exception ex)
{
// No map application available to open or placemark can not be located
}
}
}
public class MapTest3
{
public async Task NavigateToBuilding25()
{
var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { NavigationMode = NavigationMode.Driving };
await Map.OpenAsync(location, options);
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
class MediaPickerTest
{
async Task TakePhotoAsync()
{
try
{
FileResult photo = await MediaPicker.CapturePhotoAsync();
//Well, not looking good so far for truck rental. Should know for sure today. Called a dozen places, no one will rent a truck for towing, round trip, let you go to montana and back. Should know today if enterprise truck will have one or not. Otherwise I'll be calling safeco asking if I can just use the rental prices for a class b/c and see if I can find one.
string filePath = await LoadPhotoAsync(photo);
Console.WriteLine($"CapturePhotoAsync COMPLETED: { filePath }");
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature is not supported on the device
}
catch (PermissionException pEx)
{
// Permissions not granted
}
catch (Exception ex)
{
Console.WriteLine($"CapturePhotoAsync THREW: {ex.Message}");
}
}
async Task<string> LoadPhotoAsync(FileResult photo)
{
// canceled
if (photo == null)
return string.Empty;
// save the file into local storage
string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
using Stream sourceStream = await photo.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(localFilePath);
await sourceStream.CopyToAsync(localFileStream);
return localFilePath;
}
}
}

Просмотреть файл

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
using Microsoft.Maui.ApplicationModel;
namespace PlatformIntegration.Features
{
class PermissionsTest
{
public async Task SmallCheckStatus()
{
//<SmallCheckStatus>
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
//</SmallCheckStatus>
}
public async Task SmallRequestStatus()
{
//<SmallRequest>
PermissionStatus status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
//</SmallRequest>
}
public async Task SmallRequestStatusCustom()
{
#if ANDROID
//<SmallRequestCustom>
PermissionStatus status = await Permissions.RequestAsync<PlatformIntegration.Android.Permissions.ReadWriteStoragePerms>();
//</SmallRequestCustom>
#endif
}
//<CheckAndRequest>
public async Task<PermissionStatus> CheckAndRequestLocationPermission()
{
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
if (status == PermissionStatus.Granted)
return status;
if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
{
// Prompt the user to turn on in settings
// On iOS once a permission has been denied it may not be requested again from the application
return status;
}
if (Permissions.ShouldShowRationale<Permissions.LocationWhenInUse>())
{
// Prompt the user with additional information as to why the permission is needed
}
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
return status;
}
//</CheckAndRequest>
//<PermissionInstance>
#nullable enable
public async Task<Location?> GetLocationAsync()
{
PermissionStatus status = await CheckAndRequestPermissionAsync(new Permissions.LocationWhenInUse());
if (status != PermissionStatus.Granted)
{
// Notify user permission was denied
return null;
}
return await Geolocation.GetLocationAsync();
}
public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>(T permission)
where T : Permissions.BasePermission
{
PermissionStatus status = await permission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
status = await permission.RequestAsync();
return status;
}
#nullable restore
//</PermissionInstance>
public class MyPermission : Permissions.BasePermission
{
// This method checks if current status of the permission.
public override Task<PermissionStatus> CheckStatusAsync()
{
throw new System.NotImplementedException();
}
// This method is optional and a PermissionException is often thrown if a permission is not declared.
public override void EnsureDeclared()
{
throw new System.NotImplementedException();
}
// Requests the user to accept or deny a permission.
public override Task<PermissionStatus> RequestAsync()
{
throw new System.NotImplementedException();
}
// Indicates that the requestor should prompt the user as to why the app requires the permission, because the
// user has previously denied this permission.
public override bool ShouldShowRationale()
{
throw new NotImplementedException();
}
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class PreferencesTest
{
public void SetExamples()
{
// Set a string value:
Preferences.Set("first_name", "John");
// Set an numerical value:
Preferences.Set("age", 28);
// Set a boolean value:
Preferences.Set("has_pets", true);
}
public void GetExamples()
{
string firstName = Preferences.Get("first_name", "Unknown");
int age = Preferences.Get("age", -1);
bool hasPets = Preferences.Get("has_pets", false);
}
public void ContainsExamples()
{
bool hasFirstName = Preferences.ContainsKey("first_name");
}
public void RemoveKey()
{
Preferences.Remove("first_name");
}
public void Clear()
{
Preferences.Clear();
Preferences.Clear("shared_first_name");
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
namespace PlatformIntegration.Features
{
class ScreenshotTest
{
async Task<ImageSource> CaptureScreenshot()
{
IScreenshotResult screenshot = await Screenshot.CaptureAsync();
Stream stream = await screenshot.OpenReadAsync();
return ImageSource.FromStream(() => stream);
}
}
}

Просмотреть файл

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class SecureStorageTest
{
public static async Task SetValue()
{
try
{
await SecureStorage.SetAsync("oauth_token", "secret-oauth-token-value");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}
}
public static async Task GetValue()
{
try
{
string oauthToken = await SecureStorage.GetAsync("oauth_token");
if (oauthToken == null)
{
// No value is associated with the key "oauth_token"
}
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on device.
}
}
public static void RemoveValue()
{
SecureStorage.Remove("oauth_token");
}
public static void RemoveAllValues()
{
SecureStorage.RemoveAll();
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public class ShareTest
{
public async Task ShareText(string text)
{
await Share.RequestAsync(new ShareTextRequest
{
Text = text,
Title = "Share Text"
});
}
public async Task ShareUri(string uri)
{
await Share.RequestAsync(new ShareTextRequest
{
Uri = uri,
Title = "Share Web Link"
});
}
public async Task ShareFile()
{
string fn = "Attachment.txt";
string file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, "Hello World");
await Share.RequestAsync(new ShareFileRequest
{
Title = "Share text file",
File = new ShareFile(file)
});
}
public async Task ShareMultipleFiles()
{
string file1 = Path.Combine(FileSystem.CacheDirectory, "Attachment1.txt");
string file2 = Path.Combine(FileSystem.CacheDirectory, "Attachment2.txt");
File.WriteAllText(file1, "Content 1");
File.WriteAllText(file2, "Content 2");
await Share.RequestAsync(new ShareMultipleFilesRequest
{
Title = "Share multiple files",
Files = new List<ShareFile> { new ShareFile(file1), new ShareFile(file2) }
});
}
}
}

Просмотреть файл

@ -0,0 +1,49 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
public partial class SmsTest
{
public async Task SendSms(string messageText, string recipient)
{
try
{
var message = new SmsMessage(messageText, new[] { recipient });
await Sms.ComposeAsync(message);
}
catch (FeatureNotSupportedException ex)
{
// Sms is not supported on this device.
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}
public partial class SmsTest
{
public async Task SendSms(string messageText, string[] recipients)
{
try
{
var message = new SmsMessage(messageText, recipients);
await Sms.ComposeAsync(message);
}
catch (FeatureNotSupportedException ex)
{
// Sms is not supported on this device.
}
catch (Exception ex)
{
// Other error has occurred.
}
}
}
}

Просмотреть файл

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Features
{
internal class WebAuthTest
{
public async Task AuthIt()
{
try
{
WebAuthenticatorResult authResult = await WebAuthenticator.AuthenticateAsync(
new Uri("https://mysite.com/mobileauth/Microsoft"),
new Uri("myapp://"));
string accessToken = authResult?.AccessToken;
// Do something with the token
}
catch (TaskCanceledException e)
{
// Use stopped auth
}
}
public async Task AuthItSecure()
{
try
{
WebAuthenticatorResult authResult = await WebAuthenticator.AuthenticateAsync(
new WebAuthenticatorOptions()
{
Url = new Uri("https://mysite.com/mobileauth/Microsoft"),
CallbackUrl = new Uri("myapp://"),
PrefersEphemeralWebBrowserSession = true
});
string accessToken = authResult?.AccessToken;
// Do something with the token
}
catch (TaskCanceledException e)
{
// Use stopped auth
}
}
public async Task AuthItApple()
{
var scheme = "..."; // Apple, Microsoft, Google, Facebook, etc.
var authUrlRoot = "https://mysite.com/mobileauth/";
WebAuthenticatorResult result = null;
if (scheme.Equals("Apple")
&& DeviceInfo.Platform == DevicePlatform.iOS
&& DeviceInfo.Version.Major >= 13)
{
// Use Native Apple Sign In API's
result = await AppleSignInAuthenticator.AuthenticateAsync();
}
else
{
// Web Authentication flow
var authUrl = new Uri($"{authUrlRoot}{scheme}");
var callbackUrl = new Uri("myapp://");
result = await WebAuthenticator.AuthenticateAsync(authUrl, callbackUrl);
}
var authToken = string.Empty;
if (result.Properties.TryGetValue("name", out string name) && !string.IsNullOrEmpty(name))
authToken += $"Name: {name}{Environment.NewLine}";
if (result.Properties.TryGetValue("email", out string email) && !string.IsNullOrEmpty(email))
authToken += $"Email: {email}{Environment.NewLine}";
// Note that Apple Sign In has an IdToken and not an AccessToken
authToken += result?.AccessToken ?? result?.IdToken;
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PlatformIntegration.MainPage">
<ScrollView>
<VerticalStackLayout Spacing="25" Padding="30">
<Button Text="Sensors" Clicked="Sensors_Clicked" />
<Button Text="Battery" Clicked="Battery_Clicked" />
<Button Text="Device Details 1" Clicked="Details1_Clicked" />
<Button Text="Data" Clicked="Data_Clicked" />
<Button Text="App Model" Clicked="AppModel_Clicked" />
<Button Text="Communications" Clicked="Comms_Clicked" />
<Button Text="SymaticReader" Clicked="Reader_Clicked" />
<Button Text="Media" Clicked="Media_Clicked" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>

Просмотреть файл

@ -0,0 +1,57 @@
namespace PlatformIntegration;
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage()
{
InitializeComponent();
}
private void OnCounterClicked(object sender, EventArgs e)
{
}
private void Battery_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new BatteryTestPage());
}
private void Sensors_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new SensorsPage());
}
private void Details1_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new DeviceDetailsPage());
}
private void Data_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new DataPage());
}
private void AppModel_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new AppModelPage());
}
private void Comms_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new CommsPage());
}
private void Reader_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new ScreenReaderPage());
}
private void Media_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new MediaPage());
}
}

Просмотреть файл

@ -0,0 +1,107 @@
namespace PlatformIntegration;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureEssentials(essentials =>
{
essentials
.UseVersionTracking()
#if WINDOWS
.UseMapServiceToken("YOUR-KEY-HERE")
#endif
.AddAppAction("app_info", "App Info", icon: "app_info_action_icon")
.AddAppAction("battery_info", "Battery Info")
.OnAppAction(App.HandleAppActions);
})
;
return builder.Build();
}
private static class BootstrapVersionTracking
{
//<bootstrap_versiontracking>
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureEssentials(essentials =>
{
essentials.UseVersionTracking();
});
return builder.Build();
}
//</bootstrap_versiontracking>
}
private static class BootstrapAppAction
{
//<bootstrap_appaction>
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureEssentials(essentials =>
{
essentials
.AddAppAction("app_info", "App Info", icon: "app_info_action_icon")
.AddAppAction("battery_info", "Battery Info")
.OnAppAction(App.HandleAppActions);
});
return builder.Build();
}
//</bootstrap_appaction>
}
private static class BootstrapMapToken
{
//<bootstrap_maptoken>
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureEssentials(essentials =>
{
essentials.UseMapServiceToken("YOUR-API-TOKEN");
});
return builder.Build();
}
//</bootstrap_maptoken>
}
}
}

Просмотреть файл

@ -0,0 +1,128 @@
namespace PlatformIntegration;
public class MediaPage : ContentPage
{
public ImageSource ImageItem
{
get { return (ImageSource)GetValue(ImageItemProperty); }
set { SetValue(ImageItemProperty, value); }
}
public static readonly BindableProperty ImageItemProperty =
BindableProperty.Create("ImageItem", typeof(ImageSource), typeof(MediaPage), null);
public MediaPage()
{
this.BindingContext = this;
var imageControl = new Image();
imageControl.SetBinding(Image.SourceProperty, new Binding("ImageItem"));
Content = new VerticalStackLayout
{
imageControl,
new Button { Text = "Take photo",
Command = new Command(TakePhoto) },
new Button { Text = "Take screenshot",
Command = new Command(() => { ImageItem = TakeScreenshotAsync().Result; }) },
new Button { Text = "Text to speech",
Command = new Command(Speak) },
};
}
//<photo_take_and_save>
public async void TakePhoto()
{
if (MediaPicker.Default.IsCaptureSupported)
{
FileResult photo = await MediaPicker.Default.CapturePhotoAsync();
if (photo != null)
{
// save the file into local storage
string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
using Stream sourceStream = await photo.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(localFilePath);
await sourceStream.CopyToAsync(localFileStream);
}
}
}
//</photo_take_and_save>
//<screenshot>
public async Task<ImageSource> TakeScreenshotAsync()
{
if (Screenshot.Default.IsCaptureSupported)
{
IScreenshotResult screen = await Screenshot.Default.CaptureAsync();
Stream stream = await screen.OpenReadAsync();
return ImageSource.FromStream(() => stream);
}
return null;
}
//</screenshot>
//<speak>
public async void Speak() =>
await TextToSpeech.Default.SpeakAsync("Hello World");
//</speak>
//<speak_options>
public async void SpeakSettings()
{
IEnumerable<Locale> locales = await TextToSpeech.Default.GetLocalesAsync();
SpeechOptions options = new SpeechOptions()
{
Pitch = 1.5f, // 0.0 - 2.0
Volume = 0.75f, // 0.0 - 1.0
Locale = locales.FirstOrDefault()
};
await TextToSpeech.Default.SpeakAsync("How nice to meet you!", options);
}
//</speak_options>
//<speak_cancel>
CancellationTokenSource cts;
public async Task SpeakNowDefaultSettingsAsync()
{
cts = new CancellationTokenSource();
await TextToSpeech.Default.SpeakAsync("Hello World", cancelToken: cts.Token);
// This method will block until utterance finishes.
}
// Cancel speech if a cancellation token exists & hasn't been already requested.
public void CancelSpeech()
{
if (cts?.IsCancellationRequested ?? true)
return;
cts.Cancel();
}
//</speak_cancel>
//<speak_queue>
bool isBusy = false;
public void SpeakMultiple()
{
isBusy = true;
Task.WhenAll(
TextToSpeech.Default.SpeakAsync("Hello World 1"),
TextToSpeech.Default.SpeakAsync("Hello World 2"),
TextToSpeech.Default.SpeakAsync("Hello World 3"))
.ContinueWith((t) => { isBusy = false; }, TaskScheduler.FromCurrentSynchronizationContext());
}
//</speak_queue>
}

Просмотреть файл

@ -0,0 +1,81 @@
namespace PlatformIntegration;
public class NetworkingPage : ContentPage
{
public NetworkingPage()
{
this.BindingContext = this;
Content = new VerticalStackLayout
{
};
}
public void Test()
{
//<network_test>
NetworkAccess accessType = Connectivity.Current.NetworkAccess;
if (accessType == NetworkAccess.Internet)
{
// Connection to internet is available
}
//</network_test>
//<network_profiles>
IEnumerable<ConnectionProfile> profiles = Connectivity.Current.ConnectionProfiles;
if (profiles.Contains(ConnectionProfile.WiFi))
{
// Active Wi-Fi connection.
}
//</network_profiles>
}
//<network_implementation>
public class ConnectivityTest
{
public ConnectivityTest() =>
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
~ConnectivityTest() =>
Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
if (e.NetworkAccess == NetworkAccess.ConstrainedInternet)
Console.WriteLine("Internet access is available but is limited.");
else if (e.NetworkAccess != NetworkAccess.Internet)
Console.WriteLine("Internet access has been lost.");
// Log each active connection
Console.Write("Connections active: ");
foreach (var item in e.ConnectionProfiles)
{
switch (item)
{
case ConnectionProfile.Bluetooth:
Console.Write("Bluetooth");
break;
case ConnectionProfile.Cellular:
Console.Write("Cell");
break;
case ConnectionProfile.Ethernet:
Console.Write("Ethernet");
break;
case ConnectionProfile.WiFi:
Console.Write("WiFi");
break;
default:
break;
}
}
Console.WriteLine();
}
}
//</network_implementation>
}

Просмотреть файл

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration
{
internal class PermissionsPerPlatform
{
public interface IReadWritePermission
{
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
#if ANDROID
public class ReadWriteStoragePerms : Permissions.BasePlatformPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new List<(string androidPermission, bool isRuntime)>
{
(global::Android.Manifest.Permission.ReadExternalStorage, true),
(global::Android.Manifest.Permission.WriteExternalStorage, true)
}
.ToArray();
}
#endif
#if WINDOWS
public class ReadWriteStoragePerms : Permissions.BasePlatformPermission
{
}
#endif
#if IOS
#endif
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Platform;
namespace PlatformIntegration
{
internal class PlatExtensions
{
public void test1()
{
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-android;net6.0-ios;net6.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net6.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType>
<RootNamespace>PlatformIntegration</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Display name -->
<ApplicationTitle>PlatformIntegrationa</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.platformintegrationa</ApplicationId>
<ApplicationIdGuid>B668375E-5F70-4844-BB98-583AB74C06E1</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>3.0</ApplicationDisplayVersion>
<ApplicationVersion>3</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">14.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\appicon.svg" ForegroundFile="Resources\appiconfg.svg" Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\appiconfg.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<Compile Update="SensorsPage.xaml.cs">
<DependentUpon>SensorsPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<MauiXaml Update="AppModelPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="BatteryTestPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="CommsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="DataPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="DeviceDetailsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="SensorsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugFramework>net6.0-android</ActiveDebugFramework>
<IsFirstTimeProjectOpen>False</IsFirstTimeProjectOpen>
<ActiveDebugProfile>Pixel 3a - API 30 (Android 11.0 - API 30)</ActiveDebugProfile>
<SelectedDevice>pixel_3a_-_api_30</SelectedDevice>
<SelectedPlatformGroup>Emulator</SelectedPlatformGroup>
<DefaultDevice>pixel_3a_-_api_30</DefaultDevice>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0-android|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</PropertyGroup>
<ItemGroup>
<MauiXaml Update="AppModelPage.xaml">
<SubType>Designer</SubType>
</MauiXaml>
<MauiXaml Update="BatteryTestPage.xaml">
<SubType>Designer</SubType>
</MauiXaml>
<MauiXaml Update="CommsPage.xaml">
<SubType>Designer</SubType>
</MauiXaml>
<MauiXaml Update="DataPage.xaml">
<SubType>Designer</SubType>
</MauiXaml>
<MauiXaml Update="DeviceDetailsPage.xaml">
<SubType>Designer</SubType>
</MauiXaml>
<MauiXaml Update="SensorsPage.xaml">
<SubType>Designer</SubType>
</MauiXaml>
</ItemGroup>
<ItemGroup>
<None Update="Platforms\Windows\Package.appxmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31611.283
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformIntegration", "PlatformIntegration.csproj", "{C719BBF0-D151-46D4-9270-962DCE07C3BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C719BBF0-D151-46D4-9270-962DCE07C3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C719BBF0-D151-46D4-9270-962DCE07C3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C719BBF0-D151-46D4-9270-962DCE07C3BA}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{C719BBF0-D151-46D4-9270-962DCE07C3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C719BBF0-D151-46D4-9270-962DCE07C3BA}.Release|Any CPU.Build.0 = Release|Any CPU
{C719BBF0-D151-46D4-9270-962DCE07C3BA}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.VIBRATE" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https"/>
</intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="smsto" />
</intent>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel"/>
</intent>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
</manifest>

Просмотреть файл

@ -0,0 +1,25 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
namespace PlatformIntegration;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
//<OnCreate>
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Platform.Init(this, savedInstanceState);
}
//</OnCreate>
//<OnRequest>
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
//</OnRequest>
}

Просмотреть файл

@ -0,0 +1,32 @@
using Android.App;
using Android.Runtime;
//<contacts>
[assembly: UsesPermission(Android.Manifest.Permission.ReadContacts)]
//</contacts>
//<media_picker>
// Needed for Picking photo/video
[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage)]
// Needed for Taking photo/video
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
[assembly: UsesPermission(Android.Manifest.Permission.Camera)]
// Add these properties if you would like to filter out devices that do not have cameras, or set to false to make them optional
[assembly: UsesFeature("android.hardware.camera", Required = true)]
[assembly: UsesFeature("android.hardware.camera.autofocus", Required = true)]
//</media_picker>
namespace PlatformIntegration;
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlatformIntegration.Android.Permissions
{
#if ANDROID
public class ReadWriteStoragePerms: Microsoft.Maui.ApplicationModel.Permissions.BasePlatformPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new List<(string androidPermission, bool isRuntime)>
{
(global::Android.Manifest.Permission.ReadExternalStorage, true),
(global::Android.Manifest.Permission.WriteExternalStorage, true)
}.ToArray();
}
#endif
}

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>

Просмотреть файл

@ -0,0 +1,9 @@
using Foundation;
namespace PlatformIntegration;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,15 @@
using ObjCRuntime;
using UIKit;
namespace PlatformIntegration;
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using System;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
namespace PlatformIntegration;
class Program : MauiApplication
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
static void Main(string[] args)
{
var app = new Program();
app.Run(args);
}
}

Просмотреть файл

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.companyname.PlatformIntegration" version="1.0.0" api-version="7" xmlns="http://tizen.org/ns/packages">
<profile name="common" />
<ui-application appid="com.companyname.PlatformIntegration" exec="PlatformIntegration.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
<label>PlatformIntegration</label>
<icon>appicon.xhigh.png</icon>
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
</ui-application>
<shortcut-list />
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
<dependencies />
<provides-appdefined-privileges />
</manifest>

Просмотреть файл

@ -0,0 +1,8 @@
<maui:MauiWinUIApplication
x:Class="PlatformIntegration.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui="using:Microsoft.Maui"
xmlns:local="using:PlatformIntegration.WinUI">
</maui:MauiWinUIApplication>

Просмотреть файл

@ -0,0 +1,24 @@
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace PlatformIntegration.WinUI;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Publisher="CN=User Name" />
<Properties>
<PublisherDisplayName>User Name</PublisherDisplayName>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements />
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<uap:Capability Name="contacts"/>
</Capabilities>
</Package>

Просмотреть файл

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="PlatformIntegration.WinUI.app"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

Просмотреть файл

@ -0,0 +1,9 @@
using Foundation;
namespace PlatformIntegration;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>lyft</string>
<string>fb</string>
</array>
</dict>
</plist>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше