[Xamarin] App Actionsでアプリアイコンメニューを実装してみた

Xamarin.EssentialsのApp Actions機能を使って、アプリアイコンの長押し時に表示されるメニューを実装してみました。
2021.01.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Xamarin.Essentials 1.6で「App Actions」が追加されました。何かというと、アプリアイコンを長押しすると出てくるメニューを押せるアレです。

アプリアイコンメニューの様子(Android and iOS)

本記事では、このApp Actionsを実装してみました。

まずは基本となるアプリを作成する

プロジェクトを新規作成する

適当なプロジェクトを新規作成します。

Xamarin.Formsプロジェクトを新規作成する

プロジェクト名を設定する

アプリのテンプレートはポップアップを選択します。

サンプルアプリを選択する

ビルドしてアプリを起動する

ポップアップのサンプルアプリが起動しました。このサンプルアプリに対して、App Actionsを実装します。

Androidアプリ

サンプルアプリの様子(Android)

iOSアプリ

サンプルアプリの様子(iOS)

App Actionsを実装する

Xamarin.Essentialsの更新

Xamarin.Essentialsを1.6.0以上に更新します。

Xamarin.Essentialsをアップデートする

共通処理

App.xaml.csにアクションの一覧と選択された際の処理を追加します。

App.xaml.cs

using AppActionsSample.Services;
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Essentials;

namespace AppActionsSample
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

            AppActions.OnAppAction += AppActions_OnAppAction;

            DependencyService.Register<MockDataStore>();
            MainPage = new AppShell();
        }

        protected override async void OnStart()
        {
            try
            {
                await AppActions.SetAsync(
                    new AppAction("abount", "About", icon: "icon_about"),
                    new AppAction("browse", "Browse", icon: "icon_feed"),
                    new AppAction("add", "Add"));
            }
            catch (FeatureNotSupportedException ex)
            {
                Debug.WriteLine("App Actions not supported");
            }
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }

        void AppActions_OnAppAction(object sender, AppActionEventArgs e)
        {
            if (Application.Current != this && Application.Current is App app)
            {
                AppActions.OnAppAction -= app.AppActions_OnAppAction;
                return;
            }
            Device.BeginInvokeOnMainThread(async () =>
            {
                if (e.AppAction.Id == "about")
                {
                    await Shell.Current.GoToAsync("//AboutPage");
                }
                if (e.AppAction.Id == "browse")
                {
                    await Shell.Current.GoToAsync("//ItemsPage");
                }
                if (e.AppAction.Id == "add")
                {
                    await Shell.Current.GoToAsync("//ItemsPage/NewItemPage");
                }
            });
        }
    }
}

ドキュメントのサンプルコードでは、Navigationの下記遷移メソッドでしたが、Browseの場合に「Root→About→Browse」という画面スタックになっていたため、Shellを用いる方法にしています。

  • Application.Current.MainPage.Navigation.PopToRootAsync();
  • Application.Current.MainPage.Navigation.PushAsync(page);

他に良い方法があるのかもしれません。

Androidアプリの場合

MainActivity.csにIntentフィルターとOnResume()OnNewIntent()を追加します。

MainActivity.cs

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using Android.Content;

namespace AppActionsSample.Droid
{
    [IntentFilter(new[] { Xamarin.Essentials.Platform.Intent.ActionAppAction }, Categories = new[] { Android.Content.Intent.CategoryDefault })]
    [Activity(Label = "AppActionsSample", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            ...
        }

        protected override void OnResume()
        {
            base.OnResume();

            Xamarin.Essentials.Platform.OnResume(this);
        }

        protected override void OnNewIntent(Android.Content.Intent intent)
        {
            base.OnNewIntent(intent);

            Xamarin.Essentials.Platform.OnNewIntent(intent);
        }
    }
}

iOSアプリの場合

AppDelegate.csPerformActionForShortcutItem()メソッドを追加します。

AppDelegate.cs

using Foundation;
using UIKit;

namespace AppActionsSample.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            ...
        }

        public override void PerformActionForShortcutItem(UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler)
        {
            Xamarin.Essentials.Platform.PerformActionForShortcutItem(application, shortcutItem, completionHandler);
        }
    }
}

実際のアプリアイコンメニューの様子

冒頭でもあったとおり、無事に実装できました。

アプリアイコンメニュー(Android)

アプリアイコンメニューの様子(Android)

アプリアイコンメニュー(iOS)

アプリアイコンメニューの様子(iOS)

参考