fc2ブログ

記事一覧

Androidの通知にアクションボタンを表示して処理を分岐する方法 | Xamarin.Forms


今回はAndroidで通知にアクションボタンを表示する方法についてご紹介いたします。アクションボタンを押下した際には押下したボタンの種類に応じて処理を分岐することができます。通知からアプリを開いたり、メールを送信したり、データを送受信したり、様々な用途が想定できますね。


xamarin_notification_android_action_01.png


前提条件
・Windows10 Pro 64Bit
・Visual Studio 2015 Community Update3
・Xamarin 4.3.0.795 (NuGet Xamarin.Forms 2.3.4.247)
・macOS Sierra 10.12.4 / Xcode8.3.1 / Xamarin.iOS 10.6.0.10



1.定数の定義

BroadcastReceiverでアクションを識別する為の文字列を定数化します。任意のディレクトリに実装します。

Namespace AppName.Models.ConstantValues
{
    public static class IntentAction
    {
        public const string Open = "com.CompanyName.AppName.Notification.Open";
        public const string Close = "com.CompanyName.AppName.Notification.Close";
    }
public static class RequestCode
{
public const int Notification = 12345; //重複しない任意の数字
}
}

定数の値(文字列)はインテントフィルターで使用することから、他のアプリも含めて重複しない値とすることが望ましいと思います。
尚、PendingIntentに渡すRequestCodeも通知毎に可変すると通知毎に異なるExtraを渡すことができます。



2.通知の処理

以前の記事のAndroidの通知の処理に以下の記述を追加するだけでアクションボタンが表示されます。

NotificationService.cs
NotificationManager manager = (NotificationManager)_context.GetSystemService(Context.NotificationService);

//Actionボタン(開く)
Intent openIntent = null;
PendingIntent pendingIntentOpen = null;
if (Build.VERSION.SdkInt < BuildVersionCodes.S)
{
openIntent = new Intent(_context, typeof(AppName.Droid.Models.NotificationReceiver)); //明示的インテント
openIntent.SetAction(AppName.Models.ConstantValues.IntentAction.Open);
openIntent.PutExtra("NotifyId", id.ToString()); //通知を削除するためにパラメータとして通知IDを渡す
pendingIntentOpen = PendingIntentWrapper.GetBroadcast(_context, RequestCode.Notification + id, openIntent, PendingIntentFlags.UpdateCurrent);
}
else
{
openIntent = new Intent(_context, typeof(MainActivity));
openIntent.SetAction(AppName.Models.ConstantValues.IntentAction.Open);
openIntent.SetFlags(ActivityFlags.ClearTask |
ActivityFlags.ClearTop |
ActivityFlags.NewTask |
ActivityFlags.SingleTop);
openIntent.PutExtra("NotifyId", id.ToString()); //通知を削除するためにパラメータとして通知IDを渡す
pendingIntentOpen = PendingIntentWrapper.GetActivity(_context, RequestCode.Notification + id, openIntent, PendingIntentFlags.UpdateCurrent);
}
builder.AddAction(Resource.Drawable.notify_list, "Open", pendingIntentOpen);


//Actionボタン(閉じる)
//Intent closeIntent = new Intent(); //暗黙的インテント
Intent closeIntent = new Intent(Android.App.Application.Context, typeof(AppName.Droid.Models.NotificationReceiver)); //明示的インテント
closeIntent.SetAction(AppName.Models.ConstantValues.IntentAction.Close);
closeIntent.PutExtra("NotifyId", id.ToString());    //通知を削除するためにパラメータとして通知IDを渡す
PendingIntent pendingIntentClose = PendingIntentWrapper.GetBroadcast(_context, RequestCode.Notification, closeIntent, PendingIntentFlags.UpdateCurrent);
builder.AddAction(Resource.Drawable.notify_close, "Close", pendingIntentClose);

//通知を表示
manager.Notify(id, notification);
           

※アクションボタンのアイコンサイズについて
30~40pixelの画像解像度で作成します。大きすぎるとボタンからはみ出た部分は表示されません。縮小もされませんので、サイズを考慮して作成すると良いでしょう。(Androidプロジェクト\Resouece\Drawableフォルダに保存します。)
※Android8.0以降は暗黙的インテントが使用できませんので、明示的インテントに変更しています。
※2023/02/04追記
Android12 以降では BroadcastReceiver から StartActivity を呼び出すことができなくなりましたので、代わりに PendingIntent を呼び出すように条件分岐を追加しています。PendingIntentWrapper は PendingIntent の独自ラッパークラスです。詳しくは「Android 12 (S) API 31 への対応方法」の記事をご確認ください。



3.アクションを受け取る

BroadcastReceiverを継承したクラスを作成し、インテントを受信します。
intent.Actionに通知時に設定したActionボタンの文字列が取得できますので、その内容に応じて処理を分岐します。

NotificationReceiver.cs
using System;
using Android.App;
using Android.Content;
namespace AppName.Droid.Models
{
    [BroadcastReceiver]
    [IntentFilter(new[] { AppName.Models.ConstantValues.IntentAction.Open,
                          AppName.Models.ConstantValues.IntentAction.Close,
    })]
    public class NotificationReceiver : BroadcastReceiver
    {
        //通知のアクションボタン押下の処理を受信します。
        public override void OnReceive(Context context, Intent intent)
        {
            //アクションに基づき動作させる
            if (Build.VERSION.SdkInt < BuildVersionCodes.S &&
intent.Action == AppName.Models.ConstantValues.IntentAction.Open)
            {
//Openボタンを押下した場合
                //特定のページを開く
NotificationReceiver.SetPage();
            }
           if (Build.VERSION.SdkInt < BuildVersionCodes.S ||
intent.Action == AppMame.Models.ConstantValues.IntentAction.Close)
//Android11以下またはCloseボタンを押下した場合
//通知IDを取得する
int notifyid = intent.GetIntExtra("NotifyId", 0);
            //通知を削除する
NotificationReceiver.CancelNotification(context, notifyId);
}
            //Android12(S)からBROADCAST_CLOSE_SYSTEM_DIALOGSがパーミッションが必要になり、特定のアプリのみに許可されるため。 if (Build.VERSION.SdkInt < BuildVersionCodes.S) { //表示されている通知領域(通知バー)を消す Intent dialogIntent = new Intent(Intent.ActionCloseSystemDialogs); context.SendBroadcast(dialogIntent); }
        }

/// <summary>
/// アプリ起動時の設定(MainActivityからコール)
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public static void OnActivityStarted(Android.Content.Context context, Intent intent)
{
//通知IDを取得する
string notifyId = intent?.Extras?.GetString("NotifyId");
int id = 0;
if (!string.IsNullOrEmpty(notifyId))
{
int.TryParse(notifyId, out id);
}
if (id > 0)
{
//イベントトラン画面を表示する
NotificationReceiver.SetEventTranPage();
//通知を削除する
NotificationReceiver.CancelNotification(context, id);
}
}

/// <summary>
/// 特定の画面を表示する
/// </summary>
private static void SetPage()
{
//特定のページを開く
Xamarin.Forms.Page page = ((Xamarin.Forms.MasterDetailPage)((AppName.Views.StartPage)(App.Instance.MainPage))).Detail;
page.Navigation.PopToRootAsync();
page.Navigation.PushAsync(new TestPage());
}

/// <summary>
/// 通知を削除する
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
public static void CancelNotification(Android.Content.Context context, int id)
{
//通知を削除する
NotificationManager manager = (NotificationManager)context.GetSystemService(Android.Content.Context.NotificationService);
manager.Cancel(id);
}
    }
}

※特定のページを開くにはアプリが起動していることが前提です。アプリが起動しているかどうか確認して起動する方法は以前の記事にてご確認できます。
※GetStringExtraで他の通知と重複した値しか取得できない場合はRequestCodeを通知毎に可変させましょう。
※2023/02/04追記
Android12 以降では BroadcastReceiver から StartActivity を呼び出すことができなくなりました。その為、SDKバージョンの条件分岐を追加しています。


MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
//ローカル通知からの呼び出し
Task.Run(() =>
{
//Android12の場合はPendingIntentからの呼び出しなので、ここから通知に関する動作を実行します。
NotificationReceiver.OnActivityStarted(this, this.Intent);
}).ConfigureAwait(false);
}
    }
}


通知にアクションボタンを追加して処理を分岐する方法は以上で実装が可能です。
分岐した際にPCLの関数を呼び出すなどして共通化を図った方がメンテナンス性は向上しますね。

※2022/11/09追記
Android 12 (S) SDK31 以降において、android.intent.action.CLOSE_SYSTEM_DIALOGS を使用するには、特定のアプリケーションにのみ以下のパーミッションを追加することにより使用することが認められるようです。したがって、当ブログではこの権限を追加せずに Android 12 (S) SDK31 以降の処理を分岐して使用しないように変更対応しました。
詳しくは Google のホームページ「動作の変更点」をご確認ください。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
          android:installlocation="internalOnly"           
          package="com.Company.AppName">
    <uses-sdk android:targetsdkversion="31" />
    <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
</manifest>






最後までお読みいただきありがとうございます。
当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
https://itblog.dynaspo.com/blog-entry-81.html


関連記事

コメント

コメントの投稿

※名前とタイトルが入力されていないコメントでは他のコメントとの区別ができません。

 入力されていないコメントには返信しませんのであらかじめご了承くださいませ。

※ニックネームでも良いので必ずご入力ください。

    

※必ずご入力ください。

    
    

※必ずご入力ください。

※技術的な質問には環境やエラーについて正確かつ詳細にお教えください。

・正確なエラーの内容

・Windowsのバージョン番号

・Visual Studioのバージョン

・機器の型番

・アプリやソフトのバージョン

    

カテゴリ別記事一覧

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Catalina 10.15 / Mojave 10.14 / High Sierra 10.13 / Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
【データベース】
Oracle 10g/9i
SQLServer 2016/2008R2/2005/2000
SQLAnywhere 16/11/8
【BI/レポートツール】
Cognos ReportNet (IBM)
Microsoft PowerBI
ActiveReport (GrapeCity)
CrystalReport
【OCX関連】
GrapeCity InputMan SPREAD MultiRow GridView
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS jQuery IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。