20210125のAndroidに関する記事は7件です。

AndroidStudio[Material Design]を導入

<記録用>
AndroidStudioへMaterialDesignを入れてみる。

参考サイト
https://material.io/develop/android/docs/getting-started

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】EditTextのソフトウェアキーボードのボタンを変更する方法

想定読者

Android開発経験1年未満でUI作成を担当することになった方

ソフトウェアキーボードのボタンを変更する

android:imeOptions="actionSearch"

とする虫眼鏡になります!

では他にはどのようなボタンに変更されるでしょうか?
答えはここにあります!

TextView  |  Android デベロッパー  |  Android Developers

 まとめ

公式ドキュメント最高!
ただ、そこまでたどり着くのが難しいと思うのでこの記事を道しるべとしてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[WIP]Android開発基礎

1. はじめに

Android Studioで書籍管理アプリを作成しましたので、
その際に学んだ技術を備忘録として、残しておきます。
*正しくない内容も含まれているかもしれませんので、その際はご指摘頂きたく、お願いします。

2.環境

MacOS Catalina 10.15.7
Android Studio 4.1.1

3. 学んだこと一覧

  1. 画面遷移(Activity, Fragment)
  2. Action Bar関連
  3. 背面タッチによるKeyBoardの収納
  4. RecyclerView実装

1. 画面遷移

画面の遷移と言うより、画面の呼び出しが正しい表現。

  • Activity呼び出し
LoginActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)

// signUpBtnをclickしてSignUpActivityを呼び出し。
// 呼び出し後、finish()することで、LoginActivityを終了させる。
         val signUpBtn = findViewById<Button>(R.id.signup_btn)
         signUpBtn.setOnClickListener {
             val intent = Intent(this, SignUpActivity::class.java)
             startActivity(intent)
             finish()
         }
}
  • Fragment呼び出し
BookListActivity.kt(Activity->Fragment)
  adapter.setOnClickListener {
    supportFragmentManager
        .beginTransaction()
        .add(R.id.container, BookEditFragment())
        .commit()
  }
BookListFragment.kt(Fragment->Fragment)
  adapter.setOnClickListener {
    parentFragmentManager
        .beginTransaction()
        .replace(R.id.frame_layout_activity_book_list, BookEditFragment())
        .addToBackStack(null)  //Backボタンで前のFragmentに戻ることが可能
        .commit()
  }

2. Action Bar関連

  • Title/Back Arrow(←ボタン)の追加
SignUpActivity.kt
class SignUpActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sign_up)

        // Titleの表示
        setTitle(R.string.sign_up)
        // Back Arrowの表示
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }
}
  • Menuボタン(Action Barの右側ボタン)
save.xml(新規作成)
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/save"
        android:title="@string/save"
        app:showAsAction="always" />
</menu>
SignUpActivity.kt
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.save, menu)
    return true
}

参照:公式ドキュメント

  • 各種ボタンクリック時の遷移
SignUpActivity.kt
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        android.R.id.home -> { //Back Arrowクリック時、LoginActivity起動
            val intent = Intent(this, LoginActivity::class.java)
            startActivity(intent)
            finish()
            true
            }
        R.id.save -> {
            val intent = Intent(this, BookListActivity::class.java)
            startActivity(intent)
            finish()
            true
            }
        }
        else -> super.onOptionsItemSelected(item)
    }
}

参照:公式ドキュメント

3. 背面タッチによるKeyBoard収納

よく使うので、UtilsにObject化。

KeyBoard
object KeyboardUtils {
    fun hideKeyBoard(focusView: View) {
        val inputMethodManager: InputMethodManager =
            focusView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputMethodManager.hideSoftInputFromWindow(
            focusView.windowToken,
            InputMethodManager.HIDE_NOT_ALWAYS
        )
    }
}

呼び出すときは、Activityに記述。
Activityに記述しておけば、それに付随するFragmentにも適用される。

LoginActivity.kt
override fun onTouchEvent(event: MotionEvent?): Boolean {
        val focusView = currentFocus ?: return false
        KeyboardUtils.hideKeyBoard(focusView)
        return false
}

4. RecyclerView実装

長くなりそうなので、別記事で。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android開発基礎

1. はじめに

Android Studioで書籍管理アプリを作成しましたので、
その際に学んだ技術を備忘録として、残しておきます。
*正しくない内容も含まれているかもしれませんので、その際はご指摘頂きたく、お願いします。

2.環境

MacOS Catalina 10.15.7
Android Studio 4.1.1

3. 学んだこと一覧

  1. 画面遷移(Activity, Fragment)
  2. Action Bar関連

1. 画面遷移

画面の遷移と言うより、画面の呼び出しが正しい表現。

  • Activity呼び出し
LoginActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)

// signUpBtnをclickしてSignUpActivityを呼び出し。
// 呼び出し後、finish()することで、LoginActivityを終了させる。
         val signUpBtn = findViewById<Button>(R.id.signup_btn)
         signUpBtn.setOnClickListener {
             val intent = Intent(this, SignUpActivity::class.java)
             startActivity(intent)
             finish()
         }
}
  • Fragment呼び出し
BookListActivity.kt(Activity->Fragment)
  adapter.setOnClickListener {
    supportFragmentManager
        .beginTransaction()
        .add(R.id.container, BookEditFragment())
        .commit()
  }
BookListFragment.kt(Fragment->Fragment)
  adapter.setOnClickListener {
    parentFragmentManager
        .beginTransaction()
        .replace(R.id.frame_layout_activity_book_list, BookEditFragment())
        .addToBackStack(null)  //Backボタンで前のFragmentに戻ることが可能
        .commit()
  }

2. Action Bar関連

  • Title/Back Arrow(←ボタン)の追加
SignUpActivity.kt
class SignUpActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sign_up)

        // Titleの表示
        setTitle(R.string.sign_up)
        // Back Arrowの表示
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }
}
  • Menuボタン(Action Barの右側ボタン)
save.xml(新規作成)
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/save"
        android:title="@string/save"
        app:showAsAction="always" />
</menu>
SignUpActivity.kt
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.save, menu)
    return true
}
  • 各種ボタンクリック時の遷移
SignUpActivity.kt
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        android.R.id.home -> { //Back Arrowクリック時、LoginActivity起動
            val intent = Intent(this, LoginActivity::class.java)
            startActivity(intent)
            finish()
            true
            }
        R.id.save -> {
            val intent = Intent(this, BookListActivity::class.java)
            startActivity(intent)
            finish()
            true
            }
        }
        else -> super.onOptionsItemSelected(item)
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pepper SDK入門(23) QiChatbotで会話に弾性をつける

?iChatbot?

QiChatbotはTopicを使ったChatbotです。
前回QiChatを利用したい場合に使うクラスとして名前だけ紹介しましたが、今回はその内容について勉強します。
QiChatbotには主に、次のような特徴があります。

  • ローカルでの音声認識とクラウドでの音声認識が存在する
  • インターネットに接続していない場合でも、ローカルでの音声認識が可能。1
  • インターネットに接続している場合、Chatアクションはローカルとクラウドの両方で音声認識を行い、クラウドの音声認識が使えるときの方が認識精度が高い。

以下のような場合は、ローカルの音声認識を利用した方が認識精度が上がったり、効率が良かったりする場合があります。

  • 聞き取りが100語よりも少ない
  • 商品名などの固有名詞が使われている
  • ユーザの発話内容が予想できる(例えば「フラクタルねこ」と画面表示されている)
  • QiChatの機能(変数やリスナー)で、会話とPepperを連動させる
  • 多言語の音声認識に対応させる
  • 音声認識以外のユーザ操作で会話を進める

これらを踏まえて、用法用量を見ていきましょう。

QiChatbotの使いかた

TopicとQiChatbotをビルドしましょう。

//Topicを作成
Topic topic = TopicBuilder.with(qiContext)
                          .withResource(R.raw.shop)
                          .build();

//QiChatbotを作成
QiChatbot qichatbot = QiChatbotBuilder.with(qiContext)
                                      .withTopic(topic)
                                      .build();

Topicの中身はQiChat syntaxを利用してトピックファイルで作成します。統語論は使いません。

#ファイル名
topic: main()

concept:(yes) [いいね いいよ うん]
concept:(no) [やだ 却下 いや 反対 むり]
concept:(other) [ランゲルハンス島 ヤコブレフ回路]

#u:はユーザ入力。()内のユーザ入力に対して()外の返答を行う。以下の場合、ユーザが「ひまだね」と言ったら、Pepperは「宇宙っぽい井戸端会議をしましょう」と発話する。
u:(ひまだね) 宇宙っぽい井戸端会議をしましょう
#u1は直前のuの発話後にのみ有効。会話開始直後にユーザが「なにそれ」と言ってもPepperは何も反応しないが、Pepperが「宇宙っぽい井戸端会議をしましょう」言った直後に「なにそれ」と言うと、Pepperは「コンタクティーの職業病です」と返す。
    u1:(~yes) 火星の月はどっちが好きですか?
        u2:(フォボスかな) 公転周期が火星の自転周期より短いとことかかっこいいですよね
        u2:(ダイモスかな) フォボスより外側の軌道にいるとことかエモいですよね
    u1:(なにそれ) コンタクティーの職業病です
        u2:(*) 分かりました

u:([今から出かけようかな どこか行ってみようかな]) ハンギング・ロックはどうですか?
    u1:(~yes) オーバールックホテルを予約しますね^endDiscuss(picnic_1)
    u1:(~no) それでは一緒にカウチポテトしましょう^endDiscuss(picnic_2)
    u1:(_*) $1 ってどっちですか? ^stayInScope
    u1:("_~other に _[行こう 行きたい]") 交通手段を調べてみます^endDiscuss(picnic_3)

ほかにもいろいろ便利機能がありますが、qiChatの文法については別途説明します!

以下の画面は前回と同じく、これの"Mastering Chat locale"で台詞を変えてみたものです。
スクリーンショット 2021-01-14 13.23.19.png

Chat?

QiChatbotをキャンセルしてもChatはキャンセルされないため、複数のChatbotを使用している場合、他のChatbotはユーザの発話に返答できます。
会話を終了するときは、Chatを終了させるのを忘れないようにしましょう。

//QiChatbotを作成
final QiChatbot qichatbot = QiChatbotBuilder.with(qiContext)
                                            .withTopic(topic)
                                            .build();

//Chatを作成
final Chat chat = ChatBuilder.with(qiContext)
                             .withChatbot(qichatbot)
                             .build();

//Chatを非同期に実行
final Future<Void> fchat = chat.async().run();

//Qichatbotの終了時にChatを停止
qichatbot.addOnEndedListener(endReason -> {
    Log.i(TAG, "qichatbot end reason = " + endReason);
    fchat.requestCancellation();
});

❎音声認識できなかった場合❌

会話を作成するときには以下のことを考慮してくださいね。

?クラウドの音声認識が利用できず、音声認識の精度が悪い場合がある
??声が小さい、あるいはノイズや騒音があり、音声認識の精度が悪い場合がある
???ユーザによる想定外の発話(唐突な召喚魔術の呪文詠唱など)がなされる場合がある
????qiChatの変数を使うときは、変数が初期化されていない可能性がある

こういった落とし穴は、事前に埋め立てておきましょう??‍♂️

Pepper—halt!⏸

今までの記事でも何回か触れてきましたが、Pepperは発話中にその内容に応じたジェスチャーを行っています。基本的に微動だにしているのです。
それをそのままにするのも止めるのも、setSpeakingBodyLanguageで変えられます。
後者を選択する場合は、以下のようにBodyLanguageOption.DISABLEDを使ってください。

// QiChatbotによる発話中のボディーランゲージを止めたいとき
qichatbot.setSpeakingBodyLanguage(BodyLanguageOption.DISABLED);
// 聞き取り中のボディーランゲージを止めたいとき
chat.setListeningBodyLanguage(BodyLanguageOption.DISABLED);

Recommendationsの取得

QiChatbot APIを使用すると、Topicファイルから音声入力の候補を取得できます。
QiChatbotを使用するとき、Pepperが応答できる内容をディスプレイに表示するなどして、ユーザを誘導することができます。いざ対面しても何と話しかければいいのか分からない、Pepperガチ恋勢にも優しい仕様になっています。??
maxRecommendationsパラメーターは、取得してくる候補の最大量です。そこに設定した数だけ、ランダムに候補を選出してきてくれます。2
QiChatbotには、

  • globalRecommendations
  • focusedTopicRecommendations
  • scopeRecommendations

の3つの異なるRecommendationsがありますので、以下で詳しく見ていきましょう。

なお、ここでは発話の候補を分かりやすく画面に表示するため、Gitのサンプルアプリではなく新たに作成した簡易アプリを使用しています。コードはブログの末尾にサンプルとして載せておきます。

globalRecommendations

現在Focusが当たっていないTopicファイルからも候補を取得するものです。
先出のTopicファイル、main以外にglobalという名前のファイルがあり、以下のように書かれていたとします。

u:(シーラカンスもちもち) オーナメントにしよう
u:(イルカおいしい) 流線型が奇麗だよね
u:(クジラしゅわしゅわ) さっきメロンソーダの中にいたよ

すると「ひまだね」と発話した後、「今から出かけようかな」だけでなく、「シーラカンスもちもち」「イルカおいしい」「クジラしゅわしゅわ」も候補として取得してきます。

簡易アプリで何かを発話する前に貝殻のボタンを押下したところ、以下のように候補を表示してくれました。
スクリーンショット 2021-01-25 12.06.10.png

focusedTopicRecommendations

現在のTopicファイルの中から候補を取得します。
先出の例で言えば、「ひまだね」と発話した後なら「今から出かけようかな」「どこか行ってみようかな」などを候補として取得してきます。

簡易アプリで「ひまだね」と発話した後にボタンを押下したところ、以下のように候補を表示してくれました。
スクリーンショット 2021-01-25 13.19.15.png

scopeRecommendations

現在のスコープ内の会話から候補を取得します。
先出の例で言えば、「ひまだね」と発話した後なら「いいね」「いいよ」「うん」「なにそれ」などを候補として取得してきます。確実に現在の会話の流れから候補を取得したい場合は、これを使用してください。

簡易アプリで「ひまだね」と発話した後にボタンを押下したところ、以下のように候補を表示してくれました。
スクリーンショット 2021-01-25 13.14.44.png

前述のようにfocusedTopicRecommendationsは現在のTopicファイルから、scopeRecommendationsはスコープ内の会話から候補を取得します。そのため、発話前にボタンを押下しても何も表示されません。

また、Topicファイル内のワイルドカード "*"、つまり何を入力してもいいと指定したところについてはどのRecommendationsでも候補はありません。
ただしTopicファイル内の[]や{}で指定された選択肢の中からは、どのRecommendationsでもそれぞれ一つずつが候補として取得されます。(以下の画像はscopeRecommendationsで「どこか行ってみようかな」と発話してからボタンを押下した結果)
スクリーンショット 2021-01-26 9.52.57.png

あとがき

今回もPepperSDKforAndroidを参考に書かせていただきました。
さらに詳しい情報はQiChatbotのAPIリファレンスを参照してみてください。

次回はTopicとTopicStatusのお話です。TOPIX(東証株価指数)の登場はありません。
nyoronyoro-gif.gif以下にサンプルのコードを載せておきます、それでは:dizzy:

public class MainActivity extends AppCompatActivity implements RobotLifecycleCallbacks {

    private QiContext qiContext;
    private Future<Void> future;
    private QiChatbot qiChatbot;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //ボタンを設定
        setContentView(R.layout.activity_main);

        QiSDK.register(this, this);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //発話の候補を取得
                        final String test = qiChatbot.globalRecommendations(5).toString();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //TextViewに取得した発話の候補を表示
                                ((TextView)findViewById(R.id.text)).setText(test);
                            }
                        });
                    }
                }).start();
            }
        });
    }

//(中略)
    @Override
    public void onRobotFocusGained(QiContext qiContext) {
        this.qiContext = qiContext;

        //Topicを作成
        Topic mainTopic = TopicBuilder.with(qiContext)// QiContextを利用してビルダーを作成
                                      .withResource(R.raw.main)//Topicファイルを設定
                                      .build();//Topicをビルド
        Topic globalTopic = TopicBuilder.with(qiContext).withResource(R.raw.global).build();
        //Futureを作成
        this.future = QiChatbotBuilder.with(qiContext)// QiContextを利用してビルダーを作成
                                      .withTopic(mainTopic, globalTopic)//Topicファイルを設定
                                      .buildAsync()//Futureをビルド
                //andThenComposeで複数の処理を非同期に連続実行
                .andThenCompose(new Function<QiChatbot, Future<Chat>>() {
                    @Override
                    //作成されたQiChatbotを呼び出し
                    public Future<Chat> execute(QiChatbot qiChatbot) throws Throwable {
                        MainActivity.this.qiChatbot = qiChatbot;
                        return ChatBuilder.with(qiContext).withChatbot(qiChatbot).buildAsync();
                    }
                })
                .andThenCompose(new Function<Chat, Future<Void>>() {
                    @Override
                    //Chatを非同期に実行
                    public Future<Void> execute(Chat chat) throws Throwable {
                        return chat.async().run();
                    }
                });
    }

  1. セキュリティの都合で、インターネットに接続できないようなケースが考えられます。 

  2. scopeRecommendationsの末尾で述べるとおり、Topicファイル内の[]や{}で指定された選択肢の中からは、どのRecommendationsでもそれぞれ一つずつが候補として取得されます。そのため、maxRecommendationsパラメーターを5にしてもRecommendationsの種類やTopicファイルの内容によって、それ以下の数の候補しか表示されない場合があります。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MSAL(Microsoft Authentication Library)を使用したSSOにおけるアクセストークンの有効期限について

背景

上司いわく、MSALを使用してSSOを行うと、アクセストークンの有効期限が1時間だから
1時間毎に再認証が必要で、スマホ使っている人がアカウントを再選択する必要があるらしい。

個人的には、それマジ?その仕様だと実際使いづらいと思うしなんかいい感じに更新してくれるんじゃないの?
って思ったんですけど、ちゃんと調べた事は無かったので調査しました。

結論

1時間毎に再認証する必要はない。
ドキュメントから、デフォルト90日間連続して操作しなかった場合には再選択が必要になる。

ドキュメント調査

以下のリファレンスには期限切れが近いトークンを更新できると記載がある。
https://docs.microsoft.com/ja-jp/azure/active-directory/develop/msal-acquire-cache-tokens#acquiring-tokens-silently-from-the-cache

また、期限切れが近いトークンを更新することもできます (トークン キャッシュには更新トークンも含まれるため)。

また、以下のページではトークンの有効期限が記載されている。
https://docs.microsoft.com/ja-jp/azure/active-directory/develop/active-directory-configurable-token-lifetimes

デフォルト値は以下の通り
アクセストークン:1時間
更新トークン:90日間

イメージとしては ここ のブログに乗っている図がわかりやすい。(書いてあるのはADALの話だけど大きくは変わらないでしょう)

また、具体的な実装としては
acquireTokenSilentAsync(scope, authority, callback)
メソッドを呼び出すことで、アクセストークンをユーザに通知することなく取得する事ができる。
https://javadoc.io/doc/com.microsoft.identity.client/msal/2.0.2/com/microsoft/identity/client/ISingleAccountPublicClientApplication.html

Perform acquire token silent call.
If there is a valid access token in the cache, the sdk will return the access token;
If no valid access token exists, the sdk will try to find a refresh token and use the refresh token to get a new access token.
If refresh token does not exist or it fails the refresh, exception will be sent back via callback.

トークンのサイレントコールの取得を実行します。
キャッシュに有効なアクセストークンがある場合、SDKはアクセストークンを返します。
有効なアクセストークンが存在しない場合、SDKは更新トークンを見つけようとし、更新トークンを使用して新しいアクセストークンを取得します。
更新トークンが存在しないか、更新に失敗した場合、例外はコールバックを介して返送されます。

なので以下のような実装をすればトークンを自動的に更新できるはず。

ISingleAccountPublicClientApplication pca = ...; // 適当に取得する
IAccount mAccount = ... // サインインして取得
SilentAuthenticationCallback callback = ... // 適当にインターフェースを実装する
pca.acquireTokenSilentAsync({"user.read"}, mAccount.getAuthority(), callback);

結論として、1時間毎に再認証する必要はなく
デフォルトでは90日間ずっとアクセスしなかった場合に、スマホ利用者がADアカウントを再選択する必要がある。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ionic】面倒臭がりでもできる、アラーム機能

時計やスケジュール張のアプリはもちろんですが、漫画アプリやコミュニケーションツールなどなど。通知機能はいろんなスマホアプリで活用されています。

今回はその中のアラーム通知をスマホアプリで実装する方法を「ionic」というフレームワークを用いて紹介させていただきたいと思います。

アラーム設定方法

基本的にはアラームの方法は2種類あります。

  • プッシュ通知(リモート通知)
    オンラインでの通知をすることができます。例えば、アプリ提供側から新着情報をユーザに提供したい場合はこれを使って通知を送ることができます。

  • ローカル通知
    プッシュ通知とは逆にオフラインの機能になります。ユーザが自分で時間を設定して、その時間に通知を受け取りたい場合に使えます。

一般的にアラームとなるとローカル通知をイメージされる方が多いと思います。今回はそのローカルでの通知を使ったアラーム機能を紹介します。

ionic公式より

ionicの公式ページにLocal Notificationという機能があります。これを使えば簡単に実装できます。

import { LocalNotifications } from '@ionic-native/local-notifications/ngx';


constructor(private localNotifications: LocalNotifications) { }

...

// Schedule delayed notification
this.localNotifications.schedule({
   text: 'Delayed ILocalNotification',
   //現在時刻の10秒後にアラーム。1秒=1000。
   trigger: {at: new Date(new Date().getTime() + 10000)},
   led: 'FF0000',
   sound: null
});

工夫を加えて実装

やりたいこと:複数のアラームを同時にかけたい。

タスクリストなどを作成し、それぞれに時間を設定。その時間になったらそれぞれのアラームが起動し、通知がくるようなアプリを想定してコードを書いていきます。

//以下のように配列にユーザが入力した時間とタイトル(通知が来たときのメッセージ)をもつような状態にする
todoList = [
  {
    time: [15,30],
    schedule:'おやつ'
  },
  {
    time: [18,50],
    schedule:'夜ご飯'
  },
]
todoAlerm() {
    const dateHour = new Date().getHours();
    const dateMinutes = new Date().getMinutes();
    //現在時間を「分」に置き換える
    const dateTime = dateHour * 60 + dateMinutes;

    for (let i = 0; i < this.todoList.length; i++) {
      let timeHour = this.todoList[i].time[0];
      let timeMinute = this.todoList[i].time[1];
    //設定した時間を「分」に置き換える
      const setAlerm = parseInt(timeHour, 10) * 60 + parseInt(timeMinute, 10);

      //設定した時間から現在時刻の差をとり、「秒」にする
      const disTime = (setAlerm - dateTime) * 60;

      ///ionic notification のtriggerに合う形に直していく
      const datedistime = new Date().getTime() + disTime * 1000;
      const alermsettime = new Date(datedistime);

      //音声の設定(AndroidとiOSで指定する音が異なるためどちらの端末か特定している)
      //ちなみにionicのplatform機能を利用
      if (this.platform.is('android')) {
        this.isAndroid = true;
      }

      //表示するメッセージを設定
      const alermtext = this.todoList[i].schedule + 'の時間になりました!';

      //現在時刻と設定時刻との差(disTime)が0以上だったら発動、そうでなかったらアラートを出す
      if (disTime >= 0) {
        this.localNotifications.schedule([
          {
       //ここがミソ!!アラームをいくつか設定する時はidを連番にする必要がある。
            id: i + 1,
            text: alermtext,
            trigger: {at: alermsettime},
            led: 'FF0000',
       //Androidなら'file://sound.mp3'、そうでない(iOS)なら'file://beep.caf'
            sound: this.isAndroid ? 'file://sound.mp3': 'file://beep.caf'
          }
        ]);
        this.alertSetting = true;
      } else  {
        this.alertSetting = false;
        alert(this.todoList[i].imageName.kanji + 'の時間を現在時刻より後に設定してください!。');
      }
    }
  }
}

以上です!!
ポイントは主に2つです。

  1. triggerに設定する時間
    時間の形式はどんな形でも良いわけではなく、フォーマットがあります。最初は少しここでつまづきました。1秒を1000とする決まりもあるので初心者の方は注意してください。

  2. いくつか同時に仕掛けたいならidを連番にする
    for文等を利用してidを連番にしないと上手く通知が表示されないので注意しましょう。

以上に注意すれば簡単にアラームを機能をつけることができます。
ionicフレームワークは他にも機能をたくさん揃えています。ほとんどが公式サイトにコードごと載っています。それを自分なりにカスタムしてより良いアプリやサービスを作っていけるといいですね!
機会があれば是非試してみてください!

ちなみに、以下のQRコードは実際に初学者である開発者が上記のアラーム機能を使って作ったスマホアプリです。「やることリスト」という機能に使われています。
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む