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 way of opening an application is often used when deep linking into another application's custom URI schemes.
The default implementation of the ILauncher
interface is available through the Launcher.Default property. Both the ILauncher
interface and Launcher
class are contained in the Microsoft.Maui.ApplicationModel
namespace.
Important
To open the browser to a website, use the Browser API instead.
Get started
To access the launcher functionality, the following platform-specific setup is required.
If you want to use deep links to open other Android apps you should define an intent filter in your app. This can be achieved by adding the following XML to the Platforms/Android/AndroidManifest.xml file:
<activity android:name="appName" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="lyft"/>
<data android:scheme="fb"/>
</intent-filter>
</activity>
The <data>
elements are the URI schemes pre-registered with your app. You can't use schemes that aren't defined in the intent filter.
To make your app browsable from other apps declare a <data>
element with the android:scheme
attribute:
<data android:scheme="appName"/>
Open another app
To use the Launcher functionality, call the ILauncher.OpenAsync method and pass in a String or Uri representing the app to open. Optionally, the ILauncher.CanOpenAsync(Uri) 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:
bool supportsUri = await Launcher.Default.CanOpenAsync("lyft://");
if (supportsUri)
await Launcher.Default.OpenAsync("lyft://ridetype?id=lyft_line");
The previous code example can be simplified by using the TryOpenAsync(Uri), which checks if the URI scheme can be opened, before opening it:
bool launcherOpened = await Launcher.Default.TryOpenAsync("lyft://ridetype?id=lyft_line");
if (launcherOpened)
{
// Do something fun
}
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:
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)));
Set the launcher location
Important
This section only applies to iPadOS.
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:
await Share.RequestAsync(new ShareFileRequest
{
Title = Title,
File = new ShareFile(file),
PresentationSourceBounds = DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Idiom == DeviceIdiom.Tablet
? new Rect(0, 20, 0, 0)
: Rect.Zero
});
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(file),
PresentationSourceBounds = DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Idiom == DeviceIdiom.Tablet
? new Rect(0, 20, 0, 0)
: Rect.Zero
});
Everything described here works equally for Share and Launcher.
Here is an extension method that helps calculate the bounds of a view:
public static class ViewHelpers
{
public static Rect 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 Rect(absoluteX, absoluteY, element.Width, element.Height);
}
}
This can then be used when calling RequestAsync:
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:
<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.
Platform differences
This section describes the platform-specific differences with the launcher API.
The Task returned from CanOpenAsync completes immediately.