0
0

More than 3 years have passed since last update.

【続】Codelabsの通知機能の実装をViewModelを使って書き換えてみた

Posted at

前回の続き
タスク2:通知を更新またはキャンセルするを参考に通知機能を実装してみた。

update cancel
update.gif cancel.gif

実装

layout

android:enabledでボタン操作をコントロール
trueのときボタンが押せる
falseのときボタンが押せない

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <data>
        <variable
            name="viewModel"
            type="com.example.samplenotification.NotificationViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:id="@+id/notify"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Notify Me!"
            app:layout_constraintBottom_toTopOf="@+id/update"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:enabled="@{viewModel.sendButtonEnabled}"
            android:onClick="@{(v) -> viewModel.sendNotification()}"/>

        <Button
            android:id="@+id/update"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Update Me!"
            app:layout_constraintBottom_toTopOf="@+id/cancel"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/notify"
            android:enabled="@{viewModel.updateButtonEnabled}"
            android:onClick="@{(v) -> viewModel.updateNotification()}"/>

        <Button
            android:id="@+id/cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Cancel Me!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/update"
            android:enabled="@{viewModel.cancelButtonEnabled}"
            android:onClick="@{(v) -> viewModel.cancelNotification()}"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

ViewModel

ボタンコントロールの設定

ViewModel抜粋
public class NotificationViewModel extends BaseObservable {

    public boolean sendButtonEnabled = true; // 起動時はsendボタンのみ押せるようにする
    public boolean updateButtonEnabled;
    public boolean cancelButtonEnabled;

    @Bindable public boolean isSendButtonEnabled() {
        return sendButtonEnabled;
    }

    public void setSendButtonEnabled(boolean sendButtonEnabled) {
        this.sendButtonEnabled = sendButtonEnabled;
        notifyPropertyChanged(BR.sendButtonEnabled);
    }

    @Bindable public boolean isUpdateButtonEnabled() {
        return updateButtonEnabled;
    }

    public void setUpdateButtonEnabled(boolean updateButtonEnabled) {
        this.updateButtonEnabled = updateButtonEnabled;
        notifyPropertyChanged(BR.updateButtonEnabled);
    }

    @Bindable public boolean isCancelButtonEnabled() {
        return cancelButtonEnabled;
    }

    public void setCancelButtonEnabled(boolean cancelButtonEnabled) {
        this.cancelButtonEnabled = cancelButtonEnabled;
        notifyPropertyChanged(BR.cancelButtonEnabled);
    }

}

更新通知の定義

ViewModel抜粋
    public void updateNotification() {
        // 画像ファイルをビットマップに変換。 
        // 外部に既に存在している画像ファイルを読み込むには「BitmapFactory」クラスを利用する。
        Bitmap androidImage = BitmapFactory.decodeResource(view.getContext().getResources(), R.drawable.mascot_1);

        NotificationCompat.Builder notifyBuilder = getNotificationBuilder();
        // 通知ビルダーの設定内容を上書きする
        // BigPictureStyleは、NotificationCompat.Styleのサブクラス。通知の代替レイアウトを提供する。
        notifyBuilder.setStyle(new NotificationCompat.BigPictureStyle()
                .bigPicture(androidImage)
                .setBigContentTitle("通知更新!"))
                .setContentText("本文が更新されました");

        // 通知を送信する
        mNotifyManager.notify(NOTIFICATION_ID, notifyBuilder.build());
        // updateボタンを押せなくさせる
        setUpdateButtonEnabled(false);
    }

キャンセルの定義

ViewModel抜粋
    public void cancelNotification() {
        // IDに紐づく通知をキャンセルする
        mNotifyManager.cancel(NOTIFICATION_ID);

        // 各ボタンのコントロール
        setSendButtonEnabled(true);
        setUpdateButtonEnabled(false);
        setCancelButtonEnabled(false);
    }

ViewModel全文

NotificationViewModel.java
public class NotificationViewModel extends BaseObservable {

    // チャネル ID 全てのチャネルはパッケージ内で一意のIDに関連付ける
    private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
    // PendingIntentに渡す通知id(requestCode)。 通知作成,更新または削除する場合に必要になるため、通知IDは必ず保存する。
    private static final int NOTIFICATION_ID = 0;
    // 通知処理を管理してくれるオブジェクト
    private NotificationManager mNotifyManager;
    private NotificationView view;

    public boolean sendButtonEnabled = true;
    public boolean updateButtonEnabled;
    public boolean cancelButtonEnabled;

    public void setView(NotificationView view) {
        this.view = view;
    }

    @Bindable public boolean isSendButtonEnabled() {
        return sendButtonEnabled;
    }

    public void setSendButtonEnabled(boolean sendButtonEnabled) {
        this.sendButtonEnabled = sendButtonEnabled;
        notifyPropertyChanged(BR.sendButtonEnabled);
    }

    @Bindable public boolean isUpdateButtonEnabled() {
        return updateButtonEnabled;
    }

    public void setUpdateButtonEnabled(boolean updateButtonEnabled) {
        this.updateButtonEnabled = updateButtonEnabled;
        notifyPropertyChanged(BR.updateButtonEnabled);
    }

    @Bindable public boolean isCancelButtonEnabled() {
        return cancelButtonEnabled;
    }

    public void setCancelButtonEnabled(boolean cancelButtonEnabled) {
        this.cancelButtonEnabled = cancelButtonEnabled;
        notifyPropertyChanged(BR.cancelButtonEnabled);
    }

    // 通知チャネル作成(ユーザーが通知設定をカスタマイズできるようにする)
    public void createNotificationChannel() {
        mNotifyManager = (NotificationManager) view.getContext().getSystemService(NOTIFICATION_SERVICE);

        // Android 8.0(APIレベル26)より古いデバイスはカスタマイズできない。逆にAndroid8.0(APIレベル26)以上は作成しないとクラッシュする。
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel notificationChannel = new NotificationChannel(PRIMARY_CHANNEL_ID, "お知らせ", NotificationManager.IMPORTANCE_HIGH);

            // 通知機能の初期設定
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.RED);
            notificationChannel.enableVibration(true);
            notificationChannel.setDescription("お知らせの説明");

            mNotifyManager.createNotificationChannel(notificationChannel);
        }
    }

    // 送信する通知内容を定義
    private NotificationCompat.Builder getNotificationBuilder() {

        PendingIntent notificationPendingIntent = view.getNotificationContentIntent(NOTIFICATION_ID);

        // 通知ビルダー(チャネル ID の指定を必要とするコンストラクタ)の初期設定をする
        NotificationCompat.Builder notifyBuilder = new NotificationCompat.Builder(view.getContext(), PRIMARY_CHANNEL_ID)
                .setContentTitle("通知されました")
                .setContentText("ここは本文です")
                .setSmallIcon(R.drawable.ic_android)  // 通知アイコンの設定
                .setContentIntent(notificationPendingIntent) // ユーザーが通知をタップしたときに起動するインテント
                .setAutoCancel(true) // ユーザーが通知をタップしたときに通知を閉じる
                // 以下、Android 8.0(APIレベル26)より古いデバイスに対しての設定
                .setPriority(NotificationCompat.PRIORITY_HIGH) // 通知の優先度を設定。通知が複数あったとき優先度の高い通知から上に並べられる
                .setDefaults(NotificationCompat.DEFAULT_ALL); // 通知の音、バイブレーション、LEDの色パターンをデフォルト値に設定
        return notifyBuilder;
    }

    public void sendNotification() {
        NotificationCompat.Builder notifyBuilder = getNotificationBuilder();
        mNotifyManager.notify(NOTIFICATION_ID, notifyBuilder.build());
        setSendButtonEnabled(false);
        setUpdateButtonEnabled(true);
        setCancelButtonEnabled(true);
    }

    public void updateNotification() {
        // ビットマップに変換。 外部に既に存在している画像ファイルを読み込むには「BitmapFactory」クラスを利用する。
        Bitmap androidImage = BitmapFactory.decodeResource(view.getContext().getResources(), R.drawable.mascot_1);

        NotificationCompat.Builder notifyBuilder = getNotificationBuilder();
        // 通知ビルダーの設定内容を上書きする
        // BigPictureStyleは、NotificationCompat.Styleのサブクラス。通知の代替レイアウトを提供する。
        notifyBuilder.setStyle(new NotificationCompat.BigPictureStyle()
                .bigPicture(androidImage)
                .setBigContentTitle("通知更新!"))
                .setContentText("本文が更新されました");

        mNotifyManager.notify(NOTIFICATION_ID, notifyBuilder.build());
        setUpdateButtonEnabled(false);
    }

    public void cancelNotification() {
        mNotifyManager.cancel(NOTIFICATION_ID);
        setSendButtonEnabled(true);
        setUpdateButtonEnabled(false);
        setCancelButtonEnabled(false);
    }
}

Interface(前回と同じ)

NotificationView
public interface NotificationView {

    PendingIntent getNotificationContentIntent(int notificationId);

    Context getContext();
}

Activity(前回と同じ)

MainActivity.java
public class MainActivity extends AppCompatActivity implements NotificationView{

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

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        NotificationViewModel notificationViewModel = new NotificationViewModel();

        // ViewModelにActivityのViewを紐付ける
        notificationViewModel.setView(this);
        // xmlのviewModelにNotificationViewModelを紐付ける
        binding.setViewModel(notificationViewModel);

        // 通知チャネル作成
        notificationViewModel.createNotificationChannel();

    }

    // PendingIntentを使用すると、Android通知システムがコードに代わって割り当てられたアクションを実行してくれる。
    @Override
    public PendingIntent getNotificationContentIntent(int notificationId) {
        Intent notificationIntent = new Intent(this, MainActivity.class);
        // PendingIntentの作成にはコンテンツインテント(アクティビティを起動するインテント)と通知idが必要
        PendingIntent notificationPendingIntent = PendingIntent.getActivity(this,
                notificationId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        return notificationPendingIntent;
    }

    @Override
    public Context getContext() {
        return this;
    }
}

参考サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0