Firebase Cloud Messaging(FCM)のHTTP legacy protocolでsendするメモ

fcm_regacy_http.kt

import okhttp3.*

private const val URL = "https://fcm.googleapis.com/fcm/send"
private val MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8")
private val CLIENT = OkHttpClient()

fun send(serverKey: String, jsonString: String): Response =
        newCall(serverKey, jsonString).execute()

fun send(serverKey: String, jsonString: String, callback: Callback) {
    newCall(serverKey, jsonString).enqueue(callback)
}

private fun newCall(serverKey: String, jsonString: String): Call =
        CLIENT.newCall(Request.Builder()
                .url(URL)
                .header("Authorization", "key=$serverKey")
                .post(RequestBody.create(MEDIA_TYPE_JSON, jsonString))
                .build()
        )

Autosizing TextViews

TextView のフォントサイズの自動調整機能Android 8.0 (API level 26) から導入され、Support Library 26.0 以降でも採用されています。

XML

<TextView
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:maxLines="2"
    android:text="じゅげむじゅげむごこうのすりきれかいじゃりすいぎょのすいぎょうまつうんらいまつふうらいまつくうねるところにすむところ・・・"
    app:autoSizeMinTextSize="1px"
    app:autoSizeTextType="uniform" />

注意

  • support library が慮ってくれるので android.support.v7.widget.AppCompatTextView とか明示しないように。
  • app:autoSizeMinTextSize を設定しないと自動調整が行われないっぽいです。

Auto sizing が効いてない場合の表示:

f:id:beyondseeker:20180212205052p:plain

Auto sizing が効いている場合の表示:

f:id:beyondseeker:20180212205100p:plain

Data BindingでDelegates.observableを使う際に非nullの初期値を強制されたくない件

Data Binding Library で notifyPropertyChanged を利用する際、普通にやると下記のように非 null の初期値が強制されてしまう。

class MyModel : BaseObservable() {
    @get:Bindable
    var myText: String by Delegates.observable("") { _, _, _ -> notifyPropertyChanged(BR.myText) }
}

しかし、lazy のように get 時には非 null だが初期値は null にしたいというケースがある。

でも、下記のようにできるとラクチンっすね。

class MyModel : BaseObservable() {
    @get:Bindable
    var myText: String by dataBindingObservable(BR.myText)
}

ということで、以下のようなコードで対応する。

// 上記から dataBindingObservable(BR.myText) って呼ばれるやつ。
fun <T> BaseObservable.dataBindingObservable(brId: Int): ReadWriteProperty<Any?, T> = lazyObservable { _, _, _ -> notifyPropertyChanged(brId) }

/**
 * @see kotlin.properties.Delegates.observable
 */
inline fun <T> lazyObservable(crossinline onChange: (property: KProperty<*>, oldValue: T?, newValue: T) -> Unit):
        ReadWriteProperty<Any?, T> = object : LazyObservableProperty<T>() {
    override fun afterChange(property: KProperty<*>, oldValue: T?, newValue: T) = onChange(property, oldValue, newValue)
}

/**
 * @see kotlin.properties.ObservableProperty
 */
abstract class LazyObservableProperty<T> : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    protected open fun beforeChange(property: KProperty<*>, oldValue: T?, newValue: T): Boolean = true

    protected open fun afterChange(property: KProperty<*>, oldValue: T?, newValue: T) {}

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        val oldValue = this.value
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}

おまけ。

// notifyChange() を使いたい場合
fun <T> BaseObservable.dataBindingObservable(): ReadWriteProperty<Any?, T> = lazyObservable { _, _, _ -> notifyChange() }

// 初期値と brId を指定したい場合
fun <T> BaseObservable.dataBindingObservable(initialValue: T, brId: Int): ReadWriteProperty<Any?, T> = Delegates.observable(initialValue) { _, _, _ -> notifyPropertyChanged(brId) }

// 初期値を指定して notifyChange() を使いたい場合
fun <T> BaseObservable.dataBindingObservable(initialValue: T): ReadWriteProperty<Any?, T> = Delegates.observable(initialValue) { _, _, _ -> notifyChange() }

kotlinで値がprimitive wrapperのためにlateinitできない場合

'lateinit' modifier is not allowed on properties of primitive types とか怒られるので by Delegates.notNull() で凌ぎましょう。

例:

import kotlin.properties.Delegates

var i: Int by Delegates.notNull()

fun main(args: Array<String>) {
    i = 10
    println("i = $i")
}

結果:

i = 10

Data Binding Library と Google Analytics for Firebase の依存関係の件

Data Binding Library と Google Analytics for Firebase が古いライブラリを参照してるのでメモ。

手元にある support library の version として "27.0.2" を採用しているプロジェクトで、普通にビルドすると lint で下記のように怒られる。

Incompatible Gradle Versions

../../build.gradle: All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 27.0.2, 25.2.0. Examples include com.android.support:animated-vector-drawable:27.0.2 and com.android.support:support-media-compat:25.2.0

gradlew :modules:app:dependencies とかやると下記のような依存関係がわかる。

com.android.databinding:library:1.3.1
  - com.android.support:support-v4:21.0.3

com.google.firebase:firebase-analytics:11.8.0
  - com.google.android.gms:play-services-basement:11.8.0
    - com.android.support:support-v4:25.2.0

ぐぬぬ、、、

本質的な解決じゃないけど下記のように設定してlintエラーを回避するくらいしかできんよな、、、(´・ω・`)

implementation "com.android.support:support-v4:27.0.2"

RxJava2のnullの扱いとOptional

RxJava 2.0 で Observable による null の emission が非サポートになったので、その点に関してのメモ。

Javaでnullをemitしてみる

Observable<String> source = Observable.create(emitter -> {
    try {
        emitter.onNext("Alpha");
        emitter.onNext("Beta"); // io.reactivex.exceptions.OnErrorNotImplementedException: The mapper function returned a null value.
        emitter.onNext("Gamma");
        emitter.onNext("Delta");
        emitter.onNext("Epsilon");
        emitter.onComplete();
    } catch (Throwable e) {
        emitter.onError(e);
    }
});
source.map(s -> s.length() >= 5 ? s.length() : null).subscribe(s -> System.out.println("RECEIVED: " + s));

emitter.onNext("Beta"); のところで例外がスローされる。

JavaでOptionalを使ってみる

        Observable<String> source = Observable.create(emitter -> {
            try {
                emitter.onNext("Alpha");
                emitter.onNext("Beta");
                emitter.onNext("Gamma");
                emitter.onNext("Delta");
                emitter.onNext("Epsilon");
                emitter.onComplete();
            } catch (Throwable e) {
                emitter.onError(e);
            }
        });
        source.map(s -> Optional.ofNullable(s.length() >= 5 ? s.length() : null)).subscribe(optional -> System.out.println("RECEIVED: " + (optional.orElse(null))));

Optional でラップすることにより null を emit していないので例外はスローされず、以下のように出力される。

RECEIVED: 5
RECEIVED: null
RECEIVED: 5
RECEIVED: 5
RECEIVED: 7

kotlinで普通にSequencesを使った場合

        val source = listOf("Alpha", "Beta", "Gamma", "Delta", "Epsilon")
        source.map { s -> if (s.length >= 5) s.length else null }.forEach { s -> println("RECEIVED: " + s) }

普通に null が扱えるので、例外はスローされず以下のように出力される。

RECEIVED: 5
RECEIVED: null
RECEIVED: 5
RECEIVED: 5
RECEIVED: 7

ThinkPad Compact Bluetooth Keyboard with TrackPointの接続が切れるようになった時のメモ

Windows 10 で ThinkPad Compact Bluetooth Keyboard with TrackPoint(ThinkPad Bluetooth ワイヤレストラックポイントキーボード)の接続が切れるようになった時のメモ。

結論

ThinkPad 側のアップデートで IntelBluetooth ドライバをアップデートしたら治ったっぽい。(2017-08-18現在では全く問題がない)

過去の状況

2017-07-30現在では、どうにも動きが不安定で因果関係がよくわからない。数秒で切れたり(一番多い)、ディスプレイの切り替えがあると止まったり(これは再現性が高い)、ディスプレイの切り替えをやっても止まらないが翌日には止まっていたり(ほとんどない)、そんな感じ。

メモ

  1. 試してみたけど無関係だった設定その1
    • バイスマネージャー
    • Bluetooth
    • インテル(R) ワイヤレス Bluetooth(R)
    • 電源の管理
    • 『電源の節約のために、コンピューターでこのデバイスの電源をオフにできるようにする(A)』のチェックを外してOKを選択。
  2. 試してみたけど無関係だった設定その2
    • バイスマネージャー
    • ヒューマン インターフェイスバイス
    • Lenovo BT Interface Device(HID)
    • 電源の管理
    • 『電源の節約のために、コンピューターでこのデバイスの電源をオフにできるようにする(A)』のチェックを外してOKを選択。