くうと徒然なるままに

モバイルアプリを作りながらバックエンドも作っています。

Fragment で Snackbar を表示させるHelperメソッドを書いた。

暗記とか苦手なので書く。

重要なのは、 android.R.id.content で Activity の乗ってる View を取ってきてそこに表示してる

fun Fragment.showSnackbar(
    text: String,
    @BaseTransientBottomBar.Duration length: Int = Snackbar.LENGTH_SHORT
) {
    Snackbar.make(
        requireActivity().findViewById(android.R.id.content),
        text,
        length
    ).show()
}

usage

showSnackbar("寿司食べたい")

Gson で List<*> なクラスをJsonからパースするときにシュッと処理してくれる拡張関数を書いた。

ソリューションはあるけど、拡張関数でシュッと解決してくれるものがググっても出てこなかったので書きました。

ソースコード

inline fun <reified T> Gson.fromJson(
    json: String
): T {
    return this.fromJson(
        json,
        object : TypeToken<T>() {}.type
    )
}

一番シュッと呼ぶとき

これがほんと好きなんですが、既存の関数と同じインターフェイスでシュッと呼べるようになってます。

このケースでは返り値の型が指定してある場合は型を推定してくれるので型引数を指定する必要すらないです。

fun hoge(): Huga{
val json = ""
val gson = Gson()
return gson.fromJson(
   json
)
}

GsonにList なものを渡してJsonをパースしようとすると内部的にgenericsは型消去されてしまうので、ちょっと工夫してみた。

inline 展開される代わりに型消去されないで書けるのでそれを利用してみた。

ヒントをくれたまつだんぱーさん Thx.

横に長くて解像度の良いディスプレイを買った。MacBook Pro の外部ディスプレイとして使ってます。 LCD-GCWQ341XDB

横に長くて解像度の良いディスプレイを書いました。

買ったやつの特徴

  • 横に広い
  • 解像度が高い
  • USB-C に対応してる
  • HDMI に対応してる
  • リモコン付き
  • Amazonで翌日に届く

f:id:kuxumarin:20191029032941p:plain

f:id:kuxumarin:20191029032758p:plain

一応、ゲーミングとか色がなんちゃらとか書いてありますが興味なのでどうでもいいです。

買ったモチベーション

最近は毎日10時間程度プログラミング1 (趣味プログラミングを覗く)をしていて、MacBook Pro の画面を長時間見てると疲れてくるので書いました。

なぜこの機種を買ったのか

私の持っているMacBook Proは15インチなのですが、意外なことに解像度がすでに高い状態です。 画面の綺麗さとかよくわからないので一番画面を広く使える解像度、2,880 x 1,800で使ってます。(慣れてない人が使おうとすると多分目が疲れますw

www.apple.com

そのため最低限の要求として 2500以上の解像度が欲しいなぁとか思ってます。 (1920のやつはあるがとてもじゃないが耐えれなかったので chromecast を使って netflix 専用機になってます。

横に長いやつの理由

今まで普通の大きさのしか使って来なかったので冒険してみました。

実際使ってどうなのか

リモコン付きはいいぞ

私は、MacBook Proの外部ディスプレイ以外に モンハンWindows で戦略ゲーム(Steel division2) とか色々使いたいと考えてました。

そのため、他の機器を使うためにはディスプレイの下側を触って切り替える必要がありました。 しかし、リモコンが存在することで少し手間が減りました。買った当初はあまりあてにしてなかったですが、今では必需品です...

画面が広いことによる開発生産性への影響は想像以上によかった

画面が広いと色々な面で開発生産性へ影響があります。

横に広いことで、 開発の色々なシーンで役に立ってます。マルチタスクに実行できるので非常に良い。

  • AndroidアプリのUIを開発しながらView周りのコードも記述することができる
  • Bitriseの実行画面をみながら Workflow Editorでいじりながらダッシュボード画面をみながらSlackでチャットすることができる
  • 通話しながらドキュメントをみたりメモったりできる

不満な点

USB-C で接続をすると充電が20Wまでしかされない

ディスプレイ単体ではパソコンの充電が完了しないので辛いです...

そのため、充電ケーブルを別途パソコンに繋げてます。

来年から働く会社ではiMac Pro が支給されるようですが、こちらのディスプレイも申請して使えるようにしてもらいたいなぁ。。。(デスクがそこまで広いのか謎が大きいが...


  1. リモートでのアルバイト、大学の卒業研究、sechack365、趣味の開発、Androidの開発トレンド情報収拾

Android アプリでOS組み込みの音声合成エンジンを使ってTextToSpeechを行う

この前ハッカソンAndroid アプリでOS組み込みの音声合成エンジンを使って TextToSpeechを実装してたのでその時の知見を共有します。

実装方法

1. AndroidFramework に含まれている TextToSpeech Class を利用するだけです。

developer.android.com

2. ラッパークラスを作ってみた

    fun speach(text: String) {
       val  tts = TextToSpeech((context)) {
            tts.speak(text, TextToSpeech.QUEUE_ADD, null, null)
        }.apply {
            this.language = Locale.JAPAN
        }
    }

つまづきそうなところ

1. 音声合成エンジンの初期化タイミング問題

音声合成エンジンの初期化のタイミングの問題で Constructor に渡す Linstener の中で音声を発生するようにしないといけない
そのため、TextToSpeech を連続して呼び出すときに一つのインスタンスを使いまわそうとしてハマることがあると思います。

Android 10 から導入された Settings Panels を使ってみた。仕組みも少し見てみた。

Android 10 から設定画面をユーザーアプリが起動しているときに画面を遷移せずに表示することができるようになっています。

f:id:kuxumarin:20191014220329p:plain

今回はこちらようにアプリ内に設定画面の一部を表示してみたいと思います。
ここでは、設定アプリの一部を表示と書きましたが、正確には表示するための Panel Activity の中に与えられたパラメーターに応じた slice が表示されているだけです。

実際に動かすとどんな感じになるの?

以下のように下からピョコって生えてきます。

settings panel で表示することができるパネルの種類について

  1. 色々な接続設定
  2. Wifi設定
  3. NFC設定
  4. 音量設定です。

あとひとつ表示できるものがあるようでしたが、詳しくはあとで解説します。

Settings Panel の使い方について

Settings Panel は 指定のActionでIntentを作り、 startActivityするだけで表示することができます。

具体的には以下のようなコードでいけます。

connectivity_button.setOnClickListener {
    val intent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
    startActivityForResult(intent, 0)
}
wifi_button.setOnClickListener {
    val intent = Intent(Settings.Panel.ACTION_WIFI)
    startActivityForResult(intent, 0)
}
nfc_button.setOnClickListener {
    val intent = Intent(Settings.Panel.ACTION_NFC)
    startActivityForResult(intent, 0)
}
volume_button.setOnClickListener {
    val intent = Intent(Settings.Panel.ACTION_VOLUME)
    startActivity(intent)
}
media_output_button.setOnClickListener {
    val intent = Intent("com.android.settings.panel.action.MEDIA_OUTPUT")
    startActivityForResult(intent, 0)
}

Settings.Panel以下の名前空間に定義が複数あります。

さて、この定義はどこで使われるのでしょうかってことで AOSP の mirror の setting.panel の部分を見てみました。

settignの場合分けがされた場合はそれぞれのPanelが生成されます。

Panel の中では大まかに各設定項目に分けられたものがsliceとして登録されてるのをいい感じにまとめて呼び出してるだけです。

余談

その中で表示する setting の場合分けをしている部分をみてみると面白い事実がわかりました、具体的には 上記のサンプルコードですでに見えていますが、 MEDIA_OUTPUT というパネルも存在しているようです。

github.com

外部には公開していないsetting panel があるということで逆に見てみたくなりました。

ソースコードを追っていると 対応するAction文字列を見つけることができました。

android.googlesource.com

ということで、この文字列を使って Intent を生成して実行してみると(上記のサンプルの一番下のところです)どうなるでしょうか。

結果的にいうとシステムPermissionが必要なので実行できませんでした。

ということで、今度はSettingsPanel に実装されているやつから表示されているSliceを探してみました。

実際には、このsliceが表示されるようでした。

ということで脳内レンダリングして楽しんでました(ってだけ)

github.com

Android 端末で Suica に触れたときにアプリが起動してUIDを読み取れるようにする

この記事を開いた皆さんは、タイトルのように Android 端末で Suica に触れたときにアプリが起動してUIDを読み取ろうとしている奇特な人だと思います。

NfcAdapter と言うクラスを利用することであれこれをお任せすることができます。

ここで重要になってくるのは、 Suica は NFC/Type-F と言うものです。 ので、プログラムのコード内で指定するときは そのように指定する必要があります。

作りたいもの

MainActivity.kt

private var mNfcAdapter: NfcAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mNfcAdapter = NfcAdapter.getDefaultAdapter(applicationContext)
    }

    override fun onResume() {
        super.onResume()

        val intent = Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)

        val intentFilterList = arrayOf(IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED))

        // Suica は Felica(Type-F) なのでそれを指定する
        val techList = arrayOf(
            arrayOf(android.nfc.tech.NfcF::class.java.name)
        )

        mNfcAdapter?.enableForegroundDispatch(this, pendingIntent, intentFilterList, techList)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)

        intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)?.let { tag ->
            Toast.makeText(applicationContext, tag.id.toString(), Toast.LENGTH_LONG).show()
        }
    }

Android Manifest ファイルをいい感じに設定すれば大丈夫でしょう。

<uses-permission android:name="android.permission.NFC" />
---
<intent-filter>
    <action android:name="android.nfc.action.TAG_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Androidx の認証用ライブラリ, biometric を使ってみた。

重要な情報にユーザーがアクセスする前には生体認証なりを挟みたいことがよくあると思います。

そこで、今回rc01がリリースされたばかりの biometricを使って少し遊んでみました。ので、その記録を書いてみたいと思います。

実行結果

以下のように指紋認証等利用することできます。 詳細な実行結果も取得できるようでしたが、今回はそこには触れてません。

f:id:kuxumarin:20191010230519p:plain

ライブラリの取得元

implementation "androidx.biometric:biometric:1.0.0-rc01"

使い方

実際のところリファレンスを見れば全部書いてあるのでチラ見程度にみてくださいな。

developer.android.com

実際に使うときにはデバイスのOSバージョンや端末が生体認証に対応してるかなど色々コードは増えますが、よく使いそうな部分だけ書いておきます。

val prompt = BiometricPrompt(this,
    Executor { command ->
        command.run()
    },
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)
            Toast.makeText(applicationContext, "認証に成功しました", Toast.LENGTH_SHORT).show()
        }
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            super.onAuthenticationError(errorCode, errString)
            Toast.makeText(applicationContext, "エラー", Toast.LENGTH_SHORT).show()
        }
        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            Toast.makeText(applicationContext, "失敗", Toast.LENGTH_SHORT).show()
        }
    })
val promptInfo =
    BiometricPrompt.PromptInfo
        .Builder()
        .setTitle("寿司を書います")
        .setSubtitle("回転寿司 函館 花まる 銀座店")
        .setDescription("サーモン: 800円 1つ 鮪: 400円 1つ")
        .setNegativeButtonText("手に入れる")
        .build()
prompt.authenticate(promptInfo)

体重を計測したらTwitterに自分の体重を投稿するように設定する。

体重を計測したらTwitterに自分の体重を投稿したくなりませんか?私は一回だけなりました。

ということで今回は書いていきます。

使うもの

  • withing の体重計(私は、 body cardio ってやつを使いました)
  • Twitter
  • IFTTT

実際のところIFTTTにwithing の体重計の計測データを取れるトリガーがあるのでそれをTwitterにつなげるだけです。

体重計

私は、 withing というフランスの会社が作っている体重計を書いました。

この体重計は メルカリでインターンをしているときに naoyuki_sugi さんにおすすめされて買ったものです。(クソ高かった、、、けどアプリとの連携とかは良さげ

なぜ、この体重計ならできるのか

withing の体重計にはwithing のサーバーに健康状態を送信してグラフ化してくれる機能があります。

f:id:kuxumarin:20191003081928p:plain

また、サーバーに体重データが送信されたのをトリガーにしてIFTTTを起動することができます。

あとは、おなじみのようにトリガーが呼ばれたらTwitterにツイートするやつを使って組み立てるだけで完了します。

具体的には以下のような感じです。

f:id:kuxumarin:20191003081343p:plain