Android
 Computer >> コンピューター >  >> プログラミング >> Android

アプリを起動せずにAndroidの通知アクションを実装する方法

はじめに

Androidアプリでは、通知に表示されるボタン(アクションボタン)をタップしたときに、アプリ本体を開かずに処理を実行したいケースがよくあります。例えば「通知を閉じる」「アラームを停止する」などの操作です。

この記事では、PendingIntentBroadcastReceiverを組み合わせることで、アプリを開かずに通知上のアクションを処理する方法を、サンプルコード付きで段階的に解説します。

実装の仕組み

通知のアクションボタンには、Activityを開く通常のPendingIntentではなく、getBroadcast()で作成したPendingIntentを設定します。こうすることで、ボタンがタップされた際にブロードキャストが送信され、BroadcastReceiverがそれを受け取ってバックグラウンドで処理を実行できます。結果として、アプリの画面を起動することなく目的の動作を完了させられます。

ステップ1:新規プロジェクトを作成する

Android Studioで新しいプロジェクトを作成します。メニューから「File」→「New Project」を選択し、必要な情報を入力してプロジェクトを作成してください。

ステップ2:レイアウトファイル(activity_main.xml)

res/layout/activity_main.xml に以下のコードを追加します。画面中央に「create notification」ボタンを配置し、タップ時にcreateNotificationメソッドを呼び出す構成です。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:onClick="createNotification"
        android:text="create notification"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

ステップ3:MainActivityの実装

src/MainActivity.java に以下のコードを追加します。ここでのポイントは以下の通りです。

  • 通知自体をタップしたとき用に、空のIntentで作成したPendingIntentをsetContentIntent()に設定
  • 「Cancel」ボタン用には、NotificationBroadcastReceiverへ向けるブロードキャスト型のPendingIntentをaddAction()で登録
  • 通知IDをシステム時刻から生成し、後でキャンセルできるようReceiverに渡している
  • Android 8.0(APIレベル26)以降では通知チャンネルの作成が必須のため、SDK_INTの判定を行っている
package app.tutorialspoint.com.notifyme;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    public static final String NOTIFICATION_CHANNEL_ID = "10001";
    private final static String default_notification_channel_id = "default";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void createNotification(View view) {
        int NOTIFICATION_ID = (int) System.currentTimeMillis();

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(), 0);

        Intent buttonIntent = new Intent(this, NotificationBroadcastReceiver.class);
        buttonIntent.putExtra("notificationId", NOTIFICATION_ID);
        PendingIntent btPendingIntent = PendingIntent.getBroadcast(this, 0, buttonIntent, 0);

        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), default_notification_channel_id);
        mBuilder.setContentTitle("My Notification");
        mBuilder.setContentIntent(pendingIntent);
        mBuilder.addAction(R.drawable.ic_launcher_foreground, "Cancel", btPendingIntent);
        mBuilder.setContentText("Notification Listener Service Example");
        mBuilder.setSmallIcon(R.drawable.ic_launcher_foreground);
        mBuilder.setAutoCancel(true);
        mBuilder.setDeleteIntent(getDeleteIntent());

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", importance);
            mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
            assert mNotificationManager != null;
            mNotificationManager.createNotificationChannel(notificationChannel);
        }
        assert mNotificationManager != null;
        mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
    }

    protected PendingIntent getDeleteIntent() {
        Intent intent = new Intent(MainActivity.this, NotificationBroadcastReceiver.class);
        intent.setAction("notification_cancelled");
        return PendingIntent.getBroadcast(MainActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    }
}

なお、setDeleteIntent()を使うことで、ユーザーが通知をスワイプして削除した場合にもReceiver側で検知できるようにしています。

ステップ4:NotificationBroadcastReceiverの実装

src/NotificationBroadcastReceiver.java を新規作成し、以下のコードを追加します。ReceiverはIntentから通知IDを受け取り、NotificationManagerのcancel()を呼び出して該当する通知を閉じます。この処理はすべてバックグラウンドで完結し、アプリ画面は一切表示されません。

package app.tutorialspoint.com.notifyme;

import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class NotificationBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        int notificationId = intent.getIntExtra("notificationId", 0);
        // 通知をキャンセルしたい場合
        NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        manager.cancel(notificationId);
    }
}

ステップ5:AndroidManifest.xmlの設定

最後に、AndroidManifest.xmlにReceiverを宣言します。これを忘れるとブロードキャストが受け取れず、ボタンを押しても何も起こらないので注意してください。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.tutorialspoint.com.notifyme">

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <receiver
            android:name=".NotificationBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
        </receiver>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

アプリの実行と確認

それでは実際にアプリを動かしてみましょう。Android端末をPCに接続した状態で、Android StudioのツールバーにあるRunアイコンをクリックし、接続した実機を選択して実行します。

アプリが起動したら「create notification」ボタンをタップします。通知が表示されたら、そのまま通知上のCancelボタンを押してみてください。アプリを一度も開かずに通知が消えれば成功です。これは、ボタンタップがブロードキャストとしてReceiverに直接届き、バックグラウンドでcancel()が実行されているためです。

アプリを起動せずにAndroidの通知アクションを実装する方法

まとめ

通知のアクションボタンにBroadcastReceiver向けのPendingIntentを設定すれば、アプリを起動することなくバックグラウンドで任意の処理を実行できます。この手法は、タイマーやアラームアプリの「停止」ボタン、音楽プレーヤーの再生・一時停止など、さまざまな場面で応用できるので、ぜひ活用してみてください。

  1. Androidでアクションバー(Toolbar)の高さを取得する方法をステップ解説

    この記事では、Androidアプリでアクションバー(Toolbar)の高さを取得する方法を、実際のコード例とともにステップ形式で解説します。ボタンをタップすると、アクションバーの高さがピクセル単位で画面に表示されるシンプルなサンプルアプリを作成していきます。 手順1:新しいプロジェクトを作成する Android Studioを起動し、メニューから「File」→「New Project」を選択します。必要な項目をすべて入力して、新しいプロジェクトを作成してください。 手順2:activity_main.xml にコードを追加する res/layout/activity_main.xml に以下の

  2. Androidでプライバシーを守りながらFacebookを使う方法――超軽量アプリ「Tinfoil for Facebook」活用ガイド

    近年、NSA(米国家安全保障局)による監視問題やユーザーデータの共有に関する報道が相次ぎ、オンライン上での自分の情報の扱いに不安を覚える人は少なくありません。実はスマートフォンも、私たちが想像する以上にさまざまな形で行動を追跡しています。 その代表格とも言えるのがFacebookです。ネット上のあらゆる行動を追跡していることで有名ですが、アカウントを完全に削除しなくても、追跡されずにFacebookへアクセスする方法はあります。それがAndroid向け代替アプリ「Tinfoil for Facebook」です。 まず「権限」について理解しよう Androidの権限(パーミッション)は、スマート