[Android Tips] ホーム画面などで AlertDialog を表示する

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

はじめに

今回は LINE の通知のような、ホーム画面などでダイアログを表示するサンプルを実装してみたいと思います。

alert_dialog01

この実装のポイントは以下の通りです。

  1. アプリ本体の Activity から IntentService を呼び出す
  2. IntentService で数秒経ってから BroadcastReceiver に送る
  3. BroadcastReceiver から DialogFragment を表示する Activity を起動
  4. DialogFragement を使うので FragmentActivity から起動させる
  5. FragmentActivity をテーマを使って透過させることで Dialog だけ見えるようにする
  6. FragmentActivity をアプリ本体の Activity とは別タスクで起動させる

なお、以下よりアプリ本体側の Activity を MainActivity、Dialog を表示する Activity を AlertDialogActivity と呼びます。

IntentService と BroadcastReceiver を呼び出す

まずは MainActivity から IntentService を呼び BroadcastReceiver を呼び出すところまで実装します。IntentService はアプリの処理をバックグラウンドで行うために使うクラスです。ここでは1秒待機させる処理を実行させましょう (その間にホーム画面に戻り、通知を表示させる…という確認をしたいので)。onHandleIntent() は IntentService が呼び出されたときに実行されるメソッドなので、このときに数秒待機させる処理を行ったあと BroadcastReceiver を呼び出す処理を行います。

TimerIntentService.java

package jp.classmethod.android.sample.dialogonhome;

import android.app.IntentService;
import android.content.Intent;
import android.os.Handler;
import android.os.SystemClock;

public class TimerIntentService extends IntentService {

    public TimerIntentService(String name) {
        super(name);
    }

    public TimerIntentService() {
        super("TimerIntentService");
    }

    @Override
    protected void onHandleIntent(Intent data) {

        SystemClock.sleep(5000);

        Intent intent = new Intent();
        intent.setAction("TIMER_FINISHED");
        sendBroadcast(intent);
    }
}

ここではアクションを TIMER_FINISHED としました。
次に BroadcastReceiver を実装します。BroadcastReceiver はさまざまなイベント(アクション)を受け取るためのクラスです。このクラスで TIMER_FINISHED というイベントが発生したときに呼び出されるようにします。アクション名との紐付けは後述する AndroidManifest 内で定義します。ということでまずはクラスの実装です。

TimerReceiver.java

package jp.classmethod.android.sample.dialogonhome;

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

public class TimerReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent data) {
        Intent intent = new Intent(context, AlertDialogActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
        try {
            pendingIntent.send();
        } catch (PendingIntent.CanceledException e) {
            e.printStackTrace();
        }
    }
}

アクションを受け取るときに onReceive() メソッドが呼び出されるので、このときに AlertDialogActivity を起動するようにします。AlertDialogActivity の実装は次項で解説します!
ここまでで IntentService と BroadcastReceiver の実装が終わりました。あとはこのクラスをそれぞれ AndroidManifest で定義しましょう。

<service
    android:name=".TimerIntentService" />
<receiver
    android:name=".TimerReceiver">
    <intent-filter>
        <action android:name="TIMER_FINISHED" />
    </intent-filter>
</receiver>

ここまでできたらあとは MainActivity で IntentService を呼び出すようにします。

MainActivity.java

package jp.classmethod.android.sample.dialogonhome;

import android.content.Intent;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {

    private final MainActivity self = this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.post).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startService(new Intent(self, TimerIntentService.class));
            }
        });
    }
}

これで MainActivity のボタンをタップしたら IntentService が実行され、処理完了後に BroadcastReceiver に通知してくれるところまで実装が終わりました!次はダイアログを表示するところの実装です!

AlertDialog を表示する Activity の実装

まずは Fragment から実装しましょう。といっても今回は特にカスタマイズとかはせず DialogFragment を継承したとってもシンプルな Fragment になっています。Dialog が閉じるタイミングでその Dialog を表示した Activity を finish してしまうところがポイントです。

AlertDialogFragment.java

package jp.classmethod.android.sample.dialogonhome;

import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

public class AlertDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        Dialog dialog = builder.setTitle("通知").setMessage("").create();
        dialog.setCanceledOnTouchOutside(true);

        return dialog;
    }

    @Override
    public void onStop() {
        super.onStop();
        getActivity().finish();
    }
}

次にこの DialogFragment を表示する Activity を実装します。

AlertDialogActivity.java

package jp.classmethod.android.sample.dialogonhome;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class AlertDialogActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AlertDialogFragment fragment = new AlertDialogFragment();
        fragment.show(getSupportFragmentManager(), "alert_dialog");
    }
}

この Activity は実際には表示させたくないので、透過させましょう。android:Theme.Translucent を継承すると透過させることもできますが、AlertDialog のスタイルが 2 系のものになってしまうので android:Theme.Holo を継承しつつカスタマイズします。

<style name="Translucent" parent="android:Theme.Holo.Light">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@android:style/Animation</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowContentOverlay">@null</item>
</style>

あとはこの Activity の android:launchMode を singleInstance にします。こうすることで MainActivity と同タスクで起動することはありません (別タスクで起動するのでバックキーをタップしても MainActivity に戻ることがない) 。

<activity
    android:name=".AlertDialogActivity"
    android:theme="@style/Translucent"
    android:launchMode="singleInstance">
</activity>

以上で実装完了です。あとは実行すると以下のようにホーム画面に居てもどこに居ても AlertDialog で通知してくれます!

alert_dialog02

まとめ

ということで AlertDialog をホーム画面で表示する実装でした。では来期もよろしくお願いします!

  • Hideai Minami

    さっそく実行してみました!
    ホーム画面に出てるようなカンジになるのですねぇ。すごいです。
    SystemClock.Sleep(1000)だとちょっとわかりづらいので、5000くらいにしたらいいカンジでした!

    • suwa.yuki

      コメントいただきありがとうございます!
      LINEなどのようなアプリには必要な機能ですよね。ぜひ活用いただければと思います!
      1000だと確かに少し短いかも知れませんね。上記サンプルコードも5000に修正させていただきました!

      • Hideai Minami

        こちらこそありがとうございます!
        そうですね。通知する必要のあるアプリにこちらの機能があると
        とても便利です!

  • T.E.

    素晴らしい記事ありがとうございます。
    参考にさせて頂いているのですが、どうしてもうまくいかない点があるので大変恐縮ですが質問させて頂ければと思います。

    pendingIntent.send();

    の前に
    intent.putExtra(“message”, message);


    AlertDialogActivityで受け取ろうとしているのですが、

    Intent intent = getIntent();
    String massage = intent.getStringExtra(“massage”);
    でmassage がnullになってしまい、どうしてもうまくいきません。

    なにか考えられる原因などありますでしょうか?

    • suwa.yuki

      コメントいただきありがとうございます!

      intent.putExtra(“message”, message);
      String massage = intent.getStringExtra(“massage”);

      ソースコードを見ると、putExtraでは”message”というキーで渡しているのに対し、
      getStringExtraでは”massage”というキーで受け取っているのが原因だと思われます。

      • T.E.

        ありがとうございます!無事うまくいきました。
        悩みに悩んで打つ手がなくなった状態だったので、本当に助かりました。。

        • suwa.yuki

          問題が解決したようで、良かったです!
          今後ともよろしくお願いします。