Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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)));
Control file locations
Important
This section only applies to Android.
In some scenarios on Android, such as when a file is in private storage, it can be copied into the app cache which is then shared via an Android FileProvider
. However, this can unintentionally expose the entire cache and app data to an attacker. This can be prevented by adding a file provider file paths override file to your app, and ensuring that files are copied to the location specified in this file prior to sharing.
To add a file provider file paths override file to your app, add a file named microsoft_maui_essentials_fileprovider_file_paths.xml to the Platforms\Android\Resources\xml folder in your app. Therefore, the full relative file name to the project should be Platforms\Android\Resources\xml\microsoft_maui_essentials_fileprovider_file_paths.xml. Then, add XML to the file for your required paths:
<?xml version="1.0" encoding="UTF-8" ?>
<paths>
<external-path name="external_files" path="sharing-root" />
<cache-path name="internal_cache" path="sharing-root" />
<external-cache-path name="external_cache" path="sharing-root" />
</paths>
For more information about file provider paths, see FileProvider on developer.android.com.
Prior to sharing a file, you should ensure it's first written to the sharing-root folder in one of the locations from the override file:
// Write into the specific sub-directory
var dir = Path.Combine(FileSystem.CacheDirectory, "sharing-root");
Directory.CreateDirectory(dir);
var file = Path.Combine(dir, "mydata.txt");
await File.WriteAllTextAsync(file, $"My data: {count}");
// Share the file
await Launcher.OpenAsync(new OpenFileRequest
{
Title = "My data",
File = new ReadOnlyFile(file),
});
You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file <CacheDirectory>/sharing-root/mydata.txt and the shared URI is content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt
then the file provider isn't using the correct path. If the shared URI is content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt
then the file provider is using the correct path.
Warning
When sharing a file, if you receive an Java.Lang.IllegalArgumentException
, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root.
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.