20190711のAndroidに関する記事は12件です。

KotlinのDelegated Propertyを使って、Android DatabindingのBaseObservableを実装する

はじめに

こんにちは。普段はAndroidアプリの開発をしているのですが、最近KotlinのDelegated Propertyを使って実装することが増えてきました。
また、Android Databindingというデータバインディング用の公式のライブラリがありますが、バインディングの実装方法としてBaseObservableというクラスを継承して実装する方法があります。
そこで今回は、こちらのBaseObservableとKotlinのDelegated Propertyを組み合わせた実装方法が可読性的に良いなと感じたのでその方法について紹介していこうと思います。
間違っている部分あれば指摘していただけますと幸いです。

こちらを参考にしております。
https://stackoverflow.com/questions/46029570/android-data-binding-with-kotlin-baseobservable-and-a-custom-delegate

検証済みの動作環境

以下の環境で正常に動作することを確認しています。

  • Android Studio 3.4.1, 3.5 beta
  • Kotlin 1.3.11

KotlinのDelegated Property

Kotlinの言語機能としてDelegated Propertyというものがあります。日本語に訳すと「委譲プロパティ」となります。
こちらはKotlinのプロパティのset,getする際のロジックを別のクラスとして切り出し、そちらに処理を委譲させることができるものになります。
Delegated Propertyを使った実装の例はこちらになります。byキーワードを使って、クラスを記述します。こちらのクラスには、varであればgetValue()とsetValue()を、valであればgetValue()をそれぞれoperator funtionとして定義しておくことが必要になります。

class Example {
  var p: String by ReadWriteDelegate()
  val q: String by ReadOnlyDelegate()
}

// var用のDelegate用のクラス
class ReadWriteDelegate {
  // pのプロパティの値を取得時にこちらの処理を経由する
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }

  // pのプロパティの値をセット時にこちらの処理を経由する
  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
    println("$value has been assigned to '${property.name} in $thisRef.'")
  }
}

// val用のDelegate用のクラス
class ReadOnlyDelegate {
  // qのプロパティの値をセット時にこちらの処理を経由する
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }
}

また、このDelegateに使うインターフェースも用意されています。val用のReadOnlyProperty、var用のReadWritePropertyの2つが用意されているので、プロパティごとに使い分けることになると思います。

// ReadWritePropertyインターフェースを継承する
class ReadWriteDelegate : ReadWriteProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name} in $thisRef.'")
    }
}

// ReadOnlyPropertyインターフェースを継承する
class ReadOnlyDelegate : ReadOnlyProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
}

これらのコードを以下のように実行するとこうなります。参照するときにはgetValueに記述していた処理が、更新するときはsetValueに記述していた処理が実行されていることがわかると思います。

fun main() {
    val example = Example()
    println(example.p)
    println(example.q)
    example.p = "updated p"
}

↓出力結果

Example@37f8bb67, thank you for delegating 'p' to me!
Example@37f8bb67, thank you for delegating 'q' to me!
updated p has been assigned to 'p in Example@37f8bb67.'

Process finished with exit code 0

Andriod Databindingについて

次はDatabindingについて軽く説明します。Android DatabindingはAndroidにおいてデータバインディングを実現するための公式のライブラリになります。bindするオブジェクトをxmlに定義しておくと、アノテーションプロセッシングによって、Bindingクラスが生成され、xml内で@{}のキーワードの中でbindされたオブジェクトを参照することができるというものです。

詳しくは、こちらを御覧ください。
https://developer.android.com/topic/libraries/data-binding?hl=ja

BaseOvservableによる実装

Android Databindingでは、バインディングする方法として2種類の方法があります。

  • ObservableFieldを使う
  • BaseObservableを使う

今回ObservableFieldでの方法については割愛し、BaseObservableを使った方法を紹介します。BaseObservableを使う方法では、Viewにバインディングさせる対象のプロパティは、以下のようにBaseObservableというクラス内で定義しておき、@Bindable(※Kotlinでは@get:Bindable)アノテーションを付与する必要があります。また、API通信などによって取得したデータによって、バインディングしているプロパティを更新する場合、notifyPropertyChangedメソッドを使って、プロパティが更新されたことを明示的に通知する必要があります。

class UserModel : BaseObservable() {
    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

また、アプリの規模が大きくなってきたりすると、UserModelのデータを別のdataクラスとして持たせてそちらを経由させてデータを取得したい場合もあると思います。その場合は、以下のようになり、getterまで記述する必要が出てきました。

class UserModel(val userData: UserData) : BaseObservable() {
    @get:Bindable
    var firstName: String = ""
        get() = userData.firstName
        set(value) {
            userData.firstName = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        get() = userData.lastName
        set(value) {
            userData.lastName = value
            notifyPropertyChanged(BR.lastName)
        }
}

data class UserData(val firstName: String, 
                    val lastName: String)

上記のように定義されたクラスは、xml内では@{user.firstName}のように記述することで、モデルクラスのプロパティをlayoutにbindすることができます。

<?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">

    <data>
        <variable
            name="user"
            type="youmeee.co.jp.app.UserModel" />
    </data>

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

        <TextView
            android:id="@+id/firstName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"/>

        <TextView
            android:id="@+id/lastName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"/>

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

DelegateクラスでBaseObservableを実装する

本題に入ります。上記のようにBaseObservableを使ったクラスはただプロパティをbindするだけであればそこまで冗長になりませんが、参照する際に少しロジックが入ってくると、見通しが悪くなる可能性があります。
また、データの取得時に別のdataクラスを経由したり、bindする前になんらかのロジックが必要な場合があります。その際、getterとsetterを両方記述する必要があり、プロパティが多くあるViewModelをbindする場合などにおいては、毎回それぞれを書かなくてはならず、少し手間がかかりますし、ボイラープレートな記述も増えていきます。
これを解決する方法として、KotlinのDelegated Propertyが使えます。

以下のようにDelegateクラスを定義してみます。

class UserPropertyDelegate(data: String, id: Int) : ReadWriteProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return data
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        data = value
        notifyPropertyChanged(id)
    }
}

こちらを使うと、以下のように、バインディングするプロパティを記述することができます。
setterとgetterをそれぞれ定義する必要がなくなりましたし、notifyPropertyChangedを各プロパティごとに呼ぶ必要もなくなりました。なんて便利!!

class UserModel(val userData: UserData) : BaseObservable() {
    @get:Bindable
    var firstName: String by UserPropertyDelegate(userData.firstName, BR.firstName)

    @get:Bindable
    var lastName: String by UserPropertyDelegate(userData.lastName, BR.lastName)

    /* Before
      @get:Bindable
      var firstName: String = ""
          set(value) {
              field = value
              notifyPropertyChanged(BR.firstName)
          }

      @get:Bindable
      var lastName: String = ""
          set(value) {
              field = value
              notifyPropertyChanged(BR.lastName)
          }
     */
}

また、上記のプロパティでは、Stringのプロパティにしか対応していないので、ジェネリクスを使って汎用的なDelegateクラスにすることも可能です。

// ジェネリクスTを使って、汎用的にDelegateクラスを利用する
class UserPropertyDelegate<T> (data: T, id: Int) : ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return data
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        data = value
        notifyPropertyChanged(id)
    }
}

Delegates.observableも使える

https://stackoverflow.com/questions/46029570/android-data-binding-with-kotlin-baseobservable-and-a-custom-delegate

↑こちらでも紹介されていますが、Delegates.observable()というメソッドを使ってDelegated Propertyを実装する方法もあります。

Delegates.observable()は、以下のような仕様になります。
第一引数: プロパティの初期値
第二引数: プロパティが変更されたときの通知を受け取るラムダ。パラメータとして「割り当てられているプロパティ」「古い値」「新しい値」があります。。

例えば、データバインディング用のプロパティを定義し、setしたあとnotifyPropertyChanged()を呼びたいときは、以下のように記述することができます。プロパティが変更されるたびに、ラムダにてイベントを受け取れるので、ここでnotifyPropertyChanged()を呼ぶことができます。

class UserModel : BaseObservable() {
    @get:Bindable
    var firstName: String by Delegates.observable("") { prop, old, new ->
        notifyPropertyChanged(BR.firstName)
    }
}

まとめ

KotlinのDelegated Propertyの機能を使えばDatabindingのプロパティをシンプルに定義することができました。
これからもKotlinを愛でていきたいです。

参考資料

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

【Android DataBinding】BaseObservableを継承しているクラスのプロパティをKotlinのDelegated Propertyで実装する

はじめに

こんにちは。普段はAndroidアプリの開発をしているのですが、最近KotlinのDelegated Propertyを使って実装することが増えてきました。
また、Android Databindingというデータバインディング用の公式のライブラリがありますが、バインディングの実装方法としてBaseObservableというクラスを継承して実装する方法があります。
そこで今回は、こちらのBaseObservableとKotlinのDelegated Propertyを組み合わせた実装方法が可読性的に良いなと感じたのでその方法について紹介していこうと思います。
間違っている部分あれば指摘していただけますと幸いです。

こちらを参考にしております。
https://stackoverflow.com/questions/46029570/android-data-binding-with-kotlin-baseobservable-and-a-custom-delegate

検証済みの動作環境

以下の環境で正常に動作することを確認しています。

  • Android Studio 3.4.1, 3.5 beta
  • Kotlin 1.3.11

KotlinのDelegated Property

Kotlinの言語機能としてDelegated Propertyというものがあります。日本語に訳すと「委譲プロパティ」となります。
こちらはKotlinのプロパティのset,getする際のロジックを別のクラスとして切り出し、そちらに処理を委譲させることができるものになります。
Delegated Propertyを使った実装の例はこちらになります。byキーワードを使って、クラスを記述します。こちらのクラスには、varであればgetValue()とsetValue()を、valであればgetValue()をそれぞれoperator funtionとして定義しておくことが必要になります。

class Example {
  var p: String by ReadWriteDelegate()
  val q: String by ReadOnlyDelegate()
}

// var用のDelegate用のクラス
class ReadWriteDelegate {
  // pのプロパティの値を取得時にこちらの処理を経由する
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }

  // pのプロパティの値をセット時にこちらの処理を経由する
  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
    println("$value has been assigned to '${property.name} in $thisRef.'")
  }
}

// val用のDelegate用のクラス
class ReadOnlyDelegate {
  // qのプロパティの値をセット時にこちらの処理を経由する
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }
}

また、このDelegateに使うインターフェースも用意されています。val用のReadOnlyProperty、var用のReadWritePropertyの2つが用意されているので、プロパティごとに使い分けることになると思います。

// ReadWritePropertyインターフェースを継承する
class ReadWriteDelegate : ReadWriteProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name} in $thisRef.'")
    }
}

// ReadOnlyPropertyインターフェースを継承する
class ReadOnlyDelegate : ReadOnlyProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
}

これらのコードを以下のように実行するとこうなります。参照するときにはgetValueに記述していた処理が、更新するときはsetValueに記述していた処理が実行されていることがわかると思います。

fun main() {
    val example = Example()
    println(example.p)
    println(example.q)
    example.p = "updated p"
}

↓出力結果

Example@37f8bb67, thank you for delegating 'p' to me!
Example@37f8bb67, thank you for delegating 'q' to me!
updated p has been assigned to 'p in Example@37f8bb67.'

Process finished with exit code 0

Andriod Databindingについて

次はDatabindingについて軽く説明します。Android DatabindingはAndroidにおいてデータバインディングを実現するための公式のライブラリになります。bindするオブジェクトをxmlに定義しておくと、アノテーションプロセッシングによって、Bindingクラスが生成され、xml内で@{}のキーワードの中でbindされたオブジェクトを参照することができるというものです。

詳しくは、こちらを御覧ください。
https://developer.android.com/topic/libraries/data-binding?hl=ja

BaseOvservableによる実装

Android Databindingでは、バインディングする方法として2種類の方法があります。

  • ObservableFieldを使う
  • BaseObservableを使う

今回ObservableFieldでの方法については割愛し、BaseObservableを使った方法を紹介します。BaseObservableを使う方法では、Viewにバインディングさせる対象のプロパティは、以下のようにBaseObservableというクラス内で定義しておき、@Bindable(※Kotlinでは@get:Bindable)アノテーションを付与する必要があります。また、API通信などによって取得したデータによって、バインディングしているプロパティを更新する場合、notifyPropertyChangedメソッドを使って、プロパティが更新されたことを明示的に通知する必要があります。

class UserModel : BaseObservable() {
    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

また、アプリの規模が大きくなってきたりすると、UserModelのデータを別のdataクラスとして持たせてそちらを経由させてデータを取得したい場合もあると思います。その場合は、以下のようになり、getterまで記述する必要が出てきました。

class UserModel(val userData: UserData) : BaseObservable() {
    @get:Bindable
    var firstName: String = ""
        get() = userData.firstName
        set(value) {
            userData.firstName = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        get() = userData.lastName
        set(value) {
            userData.lastName = value
            notifyPropertyChanged(BR.lastName)
        }
}

data class UserData(val firstName: String, 
                    val lastName: String)

上記のように定義されたクラスは、xml内では@{user.firstName}のように記述することで、モデルクラスのプロパティをlayoutにbindすることができます。

<?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">

    <data>
        <variable
            name="user"
            type="youmeee.co.jp.app.UserModel" />
    </data>

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

        <TextView
            android:id="@+id/firstName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"/>

        <TextView
            android:id="@+id/lastName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"/>

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

DelegateクラスでBaseObservableを実装する

本題に入ります。上記のようにBaseObservableを使ったクラスはただプロパティをbindするだけであればそこまで冗長になりませんが、参照する際に少しロジックが入ってくると、見通しが悪くなる可能性があります。
また、データの取得時に別のdataクラスを経由したり、bindする前になんらかのロジックが必要な場合があります。その際、getterとsetterを両方記述する必要があり、プロパティが多くあるViewModelをbindする場合などにおいては、毎回それぞれを書かなくてはならず、少し手間がかかりますし、ボイラープレートな記述も増えていきます。
これを解決する方法として、KotlinのDelegated Propertyが使えます。

以下のようにDelegateクラスを定義してみます。

class UserPropertyDelegate(data: String, id: Int) : ReadWriteProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return data
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        data = value
        notifyPropertyChanged(id)
    }
}

こちらを使うと、以下のように、バインディングするプロパティを記述することができます。
setterとgetterをそれぞれ定義する必要がなくなりましたし、notifyPropertyChangedを各プロパティごとに呼ぶ必要もなくなりました。なんて便利!!

class UserModel(val userData: UserData) : BaseObservable() {
    @get:Bindable
    var firstName: String by UserPropertyDelegate(userData.firstName, BR.firstName)

    @get:Bindable
    var lastName: String by UserPropertyDelegate(userData.lastName, BR.lastName)

    /* Before
      @get:Bindable
      var firstName: String = ""
          set(value) {
              field = value
              notifyPropertyChanged(BR.firstName)
          }

      @get:Bindable
      var lastName: String = ""
          set(value) {
              field = value
              notifyPropertyChanged(BR.lastName)
          }
     */
}

また、上記のプロパティでは、Stringのプロパティにしか対応していないので、ジェネリクスを使って汎用的なDelegateクラスにすることも可能です。

// ジェネリクスTを使って、汎用的にDelegateクラスを利用する
class UserPropertyDelegate<T> (data: T, id: Int) : ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return data
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        data = value
        notifyPropertyChanged(id)
    }
}

Delegates.observableも使える

https://stackoverflow.com/questions/46029570/android-data-binding-with-kotlin-baseobservable-and-a-custom-delegate

↑こちらでも紹介されていますが、Delegates.observable()というメソッドを使ってDelegated Propertyを実装する方法もあります。

Delegates.observable()は、以下のような仕様になります。
第一引数: プロパティの初期値
第二引数: プロパティが変更されたときの通知を受け取るラムダ。パラメータとして「割り当てられているプロパティ」「古い値」「新しい値」がある。

例えば、データバインディング用のプロパティを定義し、setするときはnotifyPropertyChanged()を呼びたいときは、以下のように記述することができます。プロパティが変更されるたびに、ラムダにてイベントを受け取れるので、ここでnotifyPropertyChanged()を呼ぶことができます。

class UserModel : BaseObservable() {
    @get:Bindable
    var firstName: String by Delegates.observable("") { prop, old, new ->
        notifyPropertyChanged(BR.firstName)
    }
}

まとめ

KotlinのDelegated Propertyの機能を使えばDatabindingのプロパティをシンプルに定義することができました。
これからもKotlinを愛でていきたいです。

参考資料

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

Android Keystoreの概要

昨今、システム開発においてセキュリティの重要性は増しています。
Androidのセキュリティ機構としては、Android Keystoreシステムなどが挙げられますね。
このAndroid KeystoreはAndroid developersのサイトに説明があるのですが、日本語だとちょっと読みづらい所があったので、自分なりに概要をまとめてみました。

Android Keystoreシステムとは

ざっくりまとめると、「Androidデバイス内の安全な領域に暗号化キーを保管するための仕組み」と言えそうです。
暗号化キーをアプリ内にそのまま保持したりすると、ルート化された端末だと丸見えになります。
「セキュアなデータを端末内に保持する必要がある」などのセキュリティが重視される要件ではAndroid Keystoreの使用も検討しましょう。

Android Keystoreの特徴

  1. 暗号化キーはデバイスから抽出が困難なコンテナ内に保管される
  2. 特定の暗号化モードでしか暗号化キーを使用できないように設定することができる
  3. 暗号化キーを使用する際にユーザー認証を求めることができる

Android Keystoreはどうやって暗号化キーのキーマテリアル抽出を困難にしているのか

  • キーマテリアル(注1)の使用を「暗号化操作を実行するシステムプロセス」にしか許可しない
  • 「アプリプロセス」にキーマテリアルの使用を許可しない
  • Androidデバイスの安全なハードウェア(Trusted Execution Environment(TEE)、セキュア エレメント(SE)など)にキーマテリアルをバインドできるようにしている

注1… 解釈が曖昧ですが、ここでは「暗号化キー生成の元となるデータ」を指していると思われます。

暗号化キーの作成方法

以下のソースコードは、AndroidKeystoreプロバイダを利用して暗号化キーを作成する手順の一例です。
ここではRSAアルゴリズムの鍵ペアを作成しています。
共通鍵等のその他の鍵の生成方法や、暗号モードの指定方法の詳細はAndroid developersなどを参考にしましょう。
(例外処理は省略)

Android Keystoreインスタンスの取得

AndroidKeyStoreインスタンスの取得
    /*
    Keystore.getInstanceの引数に"AndroidKeyStore"を指定し、Android KeyStoreのインスタンスを取得する。
    このインスタンスはフィールドにAndroidKeyStoreSpiのインスタンスを保持する。
     */
    val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore")
    //Android KeyStoreをロードする。内部のAndroidKeystoreSpiがこのメソッドにより初期化されるため、この処理が必要
    keyStore.load(null)

鍵ペアの生成

鍵ペアの生成
    /*
    鍵ペアを作成するためのKeyPairGeneratorのインスタンスを取得する
    引数のproviderに"AndroidKeyStore"を指定することで、
    Android Keystoreに鍵ペアを作成するKeyPairGeneratorSpiインスタンスを取得する
     */
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_RSA,
        "AndroidKeyStore"
    )

    // 作成する鍵ペアのスペックを指定するためのKeyGenParameterSpecインスタンスを生成
    val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
        "hoge", //エイリアスを"hoge"に設定。Android KeyStoreからエントリーを取得する際はこのエイリアスを使用する。
        KeyProperties.PURPOSE_SIGN//鍵の使用目的を指定する。ここでは署名のみを目的とした鍵を生成。指定した目的以外で鍵を使用するとInvalidKeyExceptionが発生する
    ).run {
        //使用するダイジェストのアルゴリズムをSHA-256に限定する。これ以外のダイジェストアルゴリズムの使用は拒否される。
        setDigests(KeyProperties.DIGEST_SHA256)
        //使用するパディングモードの指定。RSA鍵ペアを生成する場合は指定必須。指定したパディングモード以外は拒否される。
        setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
        build()
    }

    //指定したKeyGenParameterSpecでKeyPairGeneratorを初期化
    kpg.initialize(parameterSpec)
    //KeyPairGeneratorで鍵ペアを生成
    val kp = kpg.generateKeyPair()

データへの署名処理

データへの署名
    //署名対象のデータ
    val str = "sign"
    val data = str.toByteArray(StandardCharsets.UTF_8)

    // Android KeyStoreのインスタンスを取得
    val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }

    //Android KeyStoreからエイリアスを指定してKeystoreエントリを取得する
    val entry = keyStore.getEntry("hoge", null)

    //署名を作成/検証するためのSignatureインスタンスを取得。引数は適宜変更必要。
    val s = Signature.getInstance("SHA256withRSA")

    //秘密鍵を指定してSignatureインスタンスを初期化する。ここでは変数entryはprivate keyであること前提
    s.initSign((entry as KeyStore.PrivateKeyEntry).privateKey)

    //Signatureインスタンスによる署名対象データの更新を実行
    s.update(data)
    //署名対象データに署名する
    val signature = s.sign()
    //署名したデータをBase64エンコード
    val result = Base64.encodeToString(signature, Base64.DEFAULT)

Android KeyStoreを利用した暗号化キー生成を実装してみましたが、触ってみると非常に奥が深いですね。TEEやSEなどの詳細も気になるところですが、今回はここまでです。

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

FireTVのリモート停止

Vimに関してだけ使おうと思ってたんですけど、手持ちのRaspberry Piのイメージが吹っ飛んで消失する度にひどい目にあうので、この際備忘録的に残しておくやつです。簡単に…Raspbianのcronにこれを引っ掛けておくと、寝落ちしても勝手に動画再生が止まります。

TL;DR

backfire.sh
#!/bin/sh
adb connect my.fireTV > /dev/null 2>&1
sleep 3s
adb shell input keyevent 223
adb kill-server

備忘録としての解説

adb connectのあと少し待ってあげないと、$ADB_VENDOR_KEYS がカラのままなので adb shell が失敗してひどく混乱します。あとはキーイベント223 (KEYCODE_SLEEP)を送って終わりです。

多くのシェルからのFireTV遠隔操作の解説で使われる adb shell input keievent 3 (キーイベント3は KEYEVENT_HOME) は、ホームに戻る際にCECコマンドを出力する設定の場合は勝手にテレビがfireTVに切り替わり、我が家では不満アリです。電源も切りたいのであれば、"cec-utils"をインストールして 、定番のecho "standby 0" | cec-client -s を足しておくのがスジかなと思います。

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

Bitrise で並列ビルド (iOS/Android)

概要

  • React Native のビルドとデプロイに Bitrise を利用している
  • iOS のビルド時間が長いせいで、 Android が完了する前に45分超えてしまう
  • そのため、 iOS/Android でビルドするワークフローを並列実行したい
    • 使っている技術が別物だから、並列実行しても問題ないのではないか

結論

並列実行は GUI ではサポートされていないが、 Bitrise CLI を使うと、超単純に並列ビルドが可能。

  • ローカルに下記のような bitrise.yml を入れてGitリポジトリに入れる
bitrise.yml
---
format_version: '6'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: react-native
workflows:
  common:
    steps:
    - yarn@0.0.8:
        inputs:
          - command: install

  ios:
    steps:
      - certificate-and-profile-installer@1.10.1: {}
      - # 色々

  android:
    steps:
      - install-missing-android-tools@2.3.5: {}
      - # 色々

  • Bitrise にて、ワークフローエディタを開き、下記を入力
#!/bin/bash
set -ex
bitrise run common --config bitrise.yml
bitrise run ios --config bitrise.yml &
bitrise run android --config bitrise.yml &
wait

全体としてはこんな感じ。

image.png

これで、ビルド時間が3分の2くらいになった。

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

Activity transitions+shared elementsで画面遷移にアニメーションつけてきた+詰まったところ

今携わっているマッチングアプリで、ActivityTransactionで画面遷移をやってみました。

poi_transition_success.gif
※こちらはデモなので私が撮影した我が家のかわいいネコチャンが表示されています。実際のアプリではかわいい女性が表示されます。

要件

  • カード選択画面(左右スワイプで選ぶ)→プロフィール詳細への移動のとき、同じ写真はスムーズなアニメーションで地続きになるようにする
  • プロフィール詳細には複数写真があるので、プロフィール詳細で選んだ写真がそのままカード選択画面でも表示されるようにする

参考サイト

Android公式サイト
https://developer.android.com/training/transitions/start-activity
Googleデベロッパーブログ
https://developers-jp.googleblog.com/2018/04/continuous-shared-element-transitions.html

その他transition animationやらいろいろ検索ででてきたサイト

実装方法

カードからプロフィールへの遷移

SharedElementsを有効にする

style.xmlで定義してあるアプリのrootThemeに

<item name="android:windowActivityTransitions">true</item>
<item name="android:windowSharedElementsUseOverlay">true</item>

を足す。

共有させたいObjectに同じtrasitionNameを指定

カード内のImageViewとプロフィール上部のImageViewのlayoutのxmlにtrasitionNameで同じ名前を指定します。

<ImageView
    android:id="@+id/profile_photo"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:transitionName="share_profile_photo_img"/>

これはプロフィール側の指定ですが、遷移元のカード側も同様にandroid:transitionName="share_profile_photo_img"を足します。
直接書くよりも、strings.xmlに引数切った方が共有しやすいと思います。

startActivityにActivityOptionsを足す

遷移タイミングのstartActivityのBundleにoptionsとしてActivityOptionsを足す。

public void startActivity (Intent intent, Bundle options)

↑に↓を足す。
https://developer.android.com/reference/android/app/ActivityOptions.html#makeSceneTransitionAnimation(android.app.Activity,%20android.util.Pair%3Candroid.view.View,%20java.lang.String%3E...)

実際のコードでは、

ActivityOptions.makeSceneTransitionAnimation(requireActivity(),android.util.Pair.create(view.photo, "share_profile_photo_img"))

をstartActivityの第二引数に渡しています。

遷移先のActivityでImageのローディングをする

↑のstartActivityのタイミングで表示するurlを渡しておきます。(実際の実装だと諸事情によりUrlのlistと選択しているurlを渡しています)
で、onCreateでsetContentView(view)後に、対象のImageViewにローディングをします。
transition_load.gif
このとき、ただロードするとロードよりも遷移が先にきてしまい、画像が用意されていないので背景の白色が一瞬うつってしまいます。
なので、ロード前に遷移を止めるActivity#postponeEnterTransition()を呼び出し、画像ロード後にActivity#startPostponedEnterTransition()を呼び出すと、白のチラツキなしに遷移ができるようになります。
https://developer.android.com/reference/android/app/Activity.html?hl=ja#postponeEnterTransition()
https://developer.android.com/reference/android/app/Activity.html?hl=ja#startPostponedEnterTransition()
多分よくある失敗がロードが失敗したタイミングで呼び出し忘れて永遠に遷移しない、だと思うのでわすれずに呼び出しましょう。

引っかかったポイント

画像がなめらかに移動せず、拡大してからちょうどいいサイズになるように見える

poi_transition_fail.gif
最初実装したときはこんな感じでした。一回画像が拡大してから移行しているように見え、スムーズじゃないですね。
原因としては、画像の拡大率。どちらのImageViewもandroid:scaleType="fitCenter"を設定していましたが、カード側が縦長、プロフィール側が横長でした。
なので、プロフィール側の画像サイズを必ず縦長にするようにして、回避しました。

プロフィールで画像を切り替えたときにカード側に伝える方法

これはActivityTransitionに直接関係ないのですが、Transitionで遷移→戻るのタイミングで、遷移を止めるActivity#postponeEnterTransition()のようなものが見当たりませんでした(あったら教えてほしい)
で、onActivityResultをつかってみたのですが、アニメーション遷移で遷移するタイミングで間に合わず、直前に表示されていた画像がチラついていました。
対応としては、EventBus的な機構を使って、カード側のFragmentをonCreate→onDestroyまでイベントを待機して、切り替えるたびに親画面に伝え、画像のIndexを切り替え、再表示タイミングでそのurlで画像を呼び出すとだいたいキャッシュされているのですぐに表示されるため、それでしのいでいます。
正直あんまりいいやり方ではないので、他にいいやり方があれば知りたいです。

簡単でしょ?

簡単じゃなかっ……
前述のデベロッパーブログに乗っているソースコードを読みつつ、適宜いろんなブログを見つつしてなんとかわかった感じです。
でも、これを入れるだけでなんかちょっと今どきのアプリ感は出るかなーと思うので、ぜひやってみてください!
poi_transition_success.gif

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

スマホのシェア率を見れるサイトのまとめ

スマホのシェア率なんてググればすぐ出てくるだろーって思ったら意外と時間かかったのでまとめました。
特にandroidの機種ごとのシェア率は全然見つからないですね…

サイト名 OS カテゴリ 集計期間 対象 集計方法
apple iOS バージョン 1日? 世界 App Store(アクセス解析?)
スマタブinfo iOS/android バージョン 直近3ヶ月? 日本 独自調査
DeviceAtlas iPhone+android 機種 四半期 国別 提携サイトのアクセス解析
statista 記事により異なる
android android バージョン 1週間 世界 Google Play ストアのアクセス解析
statcounter iOS/android バージョン/メーカー 最小で日単位 世界/国別 提携サイトのアクセス解析
webrage iPhone+android 機種 1ヶ月? 日本 アクセス解析
BCN+R android メーカー 半期 日本 販売台数
BCN+R iPhone+android 機種 1ヶ月 日本 販売台数

apple

スクリーンショット 2019-07-10 15.49.33.png
https://developer.apple.com/support/app-store/
シンプルな円グラフでわかりやすいです。

スマタブinfo

スクリーンショット 2019-07-11 11.12.26.png
http://smatabinfo.jp/os/android/index.html
iOSはバージョンごと、androidはAPKレベルごとに見れます。
また実測値は見れませんが、androidの機種ごとのシェアが5段階のランクで見れるのがいいですね。

DeviceAtlas

スクリーンショット 2019-07-10 15.22.40.png
https://deviceatlas.com/blog/most-popular-smartphones#japan
OS関係なく全機種のシェア率の上位12機種が見れます。
日本はiphoneのシェア率が高いので実質ihponeの機種ごとのランキングになってます。

statista

https://www.statista.com/search/?q=share&category=418&subCategory=481
ほとんどの記事が有料会員専用なのであまり使えないかもしれません…
集計方法は記事によって異なります。

android

スクリーンショット 2019-07-11 11.25.21.png
https://developer.android.com/about/dashboards/index.html
集計期間が2018/10/20~2018/10/26とちょっと古いです。
他に画面サイズと密度ごとのシェア率も見れます。

statcounter

スクリーンショット 2019-07-11 11.31.23.png
http://gs.statcounter.com/
一番情報量が多く使い勝手がいいサイトでした。
OSごと、バージョンごと、などなどたくさんの統計を2009年1月から最小は日単位で見ることができます。
元となるデータも200万サイト月間100億PVと申し分ないです。

webrage

スクリーンショット 2019-07-10 15.25.30.png
https://webrage.jp/techblog/sp_share/
日本でのシェア率ランキングです。
iPhoneはiphoneで一括りにされてます。

BCN+R

スクリーンショット 2019-07-11 11.35.02.png
https://www.bcnretail.com/market/contents_type=49
androidのシェア率を全国の主要家電量販店・ネットショップのPOSデータから集計しています。
アクセス解析ではなく販売台数なので注意。
目当ての記事探すのがちょっと大変かも。

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

Kotlin のプロジェクトでもライブラリが Java8 に依存していれば compileOptions の指定が必要

結論

Kotlin だけで書いてる Android のプロジェクトで Java8 が使われているライブラリを使用する場合でも、compileOptions が必要

(Kotlin だけ使ってるプロジェクトだと意識しないから忘れがち)

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

今回の現象との遭遇

material-components 1.1.0-alpha08 が出たので Kotlin だけで書いてるプロジェクトで使おうとしたら、以下の現象に遭遇しました。

  • 実機デバッグの時にビルドは通るが layout の inflate でクラッシュしている
  • assembleDebug をするとエラーが出る
Execution failed for task ':app:mergeExtDexDebug'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
   > Failed to transform artifact 'material.aar (com.google.android.material:material:1.1.0-alpha08)' to match attributes {artifactType=android-dex, dexing-enable-desugaring=false, dexing-is-debuggable=true, dexing-min-sdk=21}.

よく見たら Java8 に依存してるて書いてるからもしかしてと思ったらそうだった:sweat_smile:

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

Windows Subsytem on Linux で OpenSTF (Smartphone Test Farm) を動かす

はじめに

OpenSTF (Smartphone Test Farm) とは

https://openstf.io/
スマートフォンやタブレットを集中管理し、ブラウザから遠隔操作できるようにするオープンソースソフトウェア。
エミュレータとは違い、物理端末のスクリーンキャプチャを転送する仕組みなので動作を正確に確認できるのが特長です。
スマホアプリやモバイル向けWebページを制作する人の味方。
image.png
動かすのに結構苦労したので、誰かの助けになればと思い記事にしていきます。

試した環境

  • Windows 10 Home 1903
  • Linux には Ubuntu 18.04.2 LTS (Bionic Beaver) を使用

Windows Subsystem on Linux (Ubuntu) の インストール

特にこだわりはなかったので STF の記事が多かった Ubuntu を選択しました。

Windows の機能「Windows Subsystem for Linux」の有効化をして
image.png

Microsoft Store から Ubuntu を探してインストールすればOK。
(バージョンの記載が無いものを選んだら 18.04 が入りました)
image.png

これでもう Ubuntu は使えます。
image.png

バージョンの確認は cat /etc/os-release でできるみたいです。コピーした状態から右クリックでペーストできます。
image.png

RethinkDB のインストール

STF ではデータベースとして RethinkDB を使用するので、先に RethinkDB を入れます。
Linux 上にインストールする方法もあるのですが Linux がわからなすぎてハマりました。
RethinkDB は Windows 上でも動くので、Linux に不慣れな方は Windows 上にインストールするのをお勧めします。
https://www.rethinkdb.com/docs/install/windows/
からダウンロードしてきて
image.png
任意のフォルダに展開し、rethinkdb.exe を起動するだけです。
(exeファイルをダブルクリックでも、コマンドプロンプトから実行でもOKです)
image.png
このように表示されれば起動中です。
http://localhost:8080 にアクセスすれば RethinkDBが動いているのを確認できると思います。
Tables や Indexes が 0 でないのは STF をインストール後にスクリーンキャプチャしたためです。
手順通り進めた場合、この段階では 0 で大丈夫です。
image.png

rethinkdb がエラーになる/ページがエラーになる場合は?

RethinkDB はポート 8080 を使用します。
他で使用していないか確認し、ある場合は他の設定を解除してから試してみてください
(色々入れてる環境で起きがち…)

STF に必要な他のアプリと、STF 本体のインストールする

RethinkDB 以外にもいくつかアプリが必要です。
これらは Linux 上にインストールしていきましょう。

こちらのお二方の記事を参考に進めました。
http://yakisaba.hatenablog.jp/entry/2018/07/26/103620
https://qiita.com/tanaka512/items/4792931fe08b08441dc7

以下全部コピーして貼り付ければOK

Ubuntu
sudo apt-get update
sudo apt-get install git
sudo apt-get install npm
sudo apt-get install android-tools-adb
sudo apt-get install python
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool
sudo apt-get install build-essential
sudo apt-get install ninja-build
sudo apt-get install libzmq3-dev
sudo apt-get install libprotobuf-dev
sudo apt-get install graphicsmagick
sudo apt-get install yasm
sudo apt-get install stow

#nodejs
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs

sudo npm install -g bower karma gulp

mkdir ~/Downloads

#ZeroMQ
cd ~/Downloads
wget http://download.zeromq.org/zeromq-4.1.2.tar.gz
tar -zxvf zeromq-4.1.2.tar.gz
cd zeromq-4.1.2
./configure --without-libsodium --prefix=/usr/local/stow/zeromq-4.1.2
make
sudo make install
cd /usr/local/stow
sudo stow -vv zeromq-4.1.2

#Google protobuf
cd ~/Downloads
git clone https://github.com/google/protobuf.git
cd protobuf
./autogen.sh
./configure --prefix=/usr/local/stow/protobuf-`git rev-parse --short HEAD`
make
sudo make install
cd /usr/local/stow
sudo stow -vv protobuf-*

sudo ldconfig

#OpenSTF
cd
mkdir openstf
cd openstf/
git clone https://github.com/openstf/stf.git
cd stf
sudo npm install -g stf --unsafe-perm

STF の実行

さきほど書いた手順で RethinkDB を起動しておきます。
そのうえで Ubuntu上 で stf local を実行します。(どの階層で実行してもOK)
下のような実行結果になるはず。

image.png

http://localhost:7100
にアクセスして次のページが表示されれば STF の起動は成功です。

image.png

STF 上で端末を操作する

端末が認識されない場合

基本的なところの確認はこちらのページがとても詳しくてわかりやすいです。
[Android] デバッグモードで接続した端末がadb devicesで認識されない場合の対処法

Windows Subsystem in Linux 特有の確認ポイント

Windows と Ubuntu で同じバージョンの adb を起動する必要があります。
Ubuntu 上の adb のバージョンは adb --version で確認できます。

image.png

上の画面だと、1.0.39 です。
このバージョンと同じ adb を探します。
一覧のページが全く見つけられなかったのですが URL でバージョンを直接指定する方法で乗り切れます。

例えば
http://dl.google.com/android/repository/platform-tools_r23.0.1-windows.zip
 → 1.0.41
http://dl.google.com/android/repository/platform-tools_r21.0.1-windows.zip
 → 1.0.39

ダウンロードできたら解凍してインストールしてください。
インストールできたら、PowerShell で adb start-server します。

image.png

うまくいかないときは、kill-server をしてから start-server を試してみてください。
また、この操作をすると Linux 上で STF が終了するので、再度 stf local を実行して起動しなおしてください。

うまくいけば、STF 上に端末が表示されるはずです。

以上です。おつかれさまでした!

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

Windows Subsystem on Linux で OpenSTF (Smartphone Test Farm) を動かす

はじめに

OpenSTF (Smartphone Test Farm) とは

https://openstf.io/
スマートフォンやタブレットを集中管理し、ブラウザから遠隔操作できるようにするオープンソースソフトウェア。
エミュレータとは違い、物理端末のスクリーンキャプチャを転送する仕組みなので動作を正確に確認できるのが特長です。
スマホアプリやモバイル向けWebページを制作する人の味方。
image.png
動かすのに結構苦労したので、誰かの助けになればと思い記事にしていきます。

試した環境

  • Windows 10 Home 1903
  • Linux には Ubuntu 18.04.2 LTS (Bionic Beaver) を使用

Windows Subsystem on Linux (Ubuntu) の インストール

特にこだわりはなかったので STF の記事が多かった Ubuntu を選択しました。

Windows の機能「Windows Subsystem for Linux」の有効化をして
image.png

Microsoft Store から Ubuntu を探してインストールすればOK。
(バージョンの記載が無いものを選んだら 18.04 が入りました)
image.png

これでもう Ubuntu は使えます。
image.png

バージョンの確認は cat /etc/os-release でできるみたいです。コピーした状態から右クリックでペーストできます。
image.png

RethinkDB のインストール

STF ではデータベースとして RethinkDB を使用するので、先に RethinkDB を入れます。
Linux 上にインストールする方法もあるのですが Linux がわからなすぎてハマりました。
RethinkDB は Windows 上でも動くので、Linux に不慣れな方は Windows 上にインストールするのをお勧めします。
https://www.rethinkdb.com/docs/install/windows/
からダウンロードしてきて
image.png
任意のフォルダに展開し、rethinkdb.exe を起動するだけです。
(exeファイルをダブルクリックでも、コマンドプロンプトから実行でもOKです)
image.png
このように表示されれば起動中です。
http://localhost:8080 にアクセスすれば RethinkDBが動いているのを確認できると思います。
Tables や Indexes が 0 でないのは STF をインストール後にスクリーンキャプチャしたためです。
手順通り進めた場合、この段階では 0 で大丈夫です。
image.png

rethinkdb がエラーになる/ページがエラーになる場合は?

RethinkDB はポート 8080 を使用します。
他で使用していないか確認し、ある場合は他の設定を解除してから試してみてください
(色々入れてる環境で起きがち…)

STF に必要な他のアプリと、STF 本体のインストールする

RethinkDB 以外にもいくつかアプリが必要です。
これらは Linux 上にインストールしていきましょう。

こちらのお二方の記事を参考に進めました。
http://yakisaba.hatenablog.jp/entry/2018/07/26/103620
https://qiita.com/tanaka512/items/4792931fe08b08441dc7

以下全部コピーして貼り付ければOK

Ubuntu
sudo apt-get update
sudo apt-get install git
sudo apt-get install npm
sudo apt-get install android-tools-adb
sudo apt-get install python
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool
sudo apt-get install build-essential
sudo apt-get install ninja-build
sudo apt-get install libzmq3-dev
sudo apt-get install libprotobuf-dev
sudo apt-get install graphicsmagick
sudo apt-get install yasm
sudo apt-get install stow

#nodejs
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs

sudo npm install -g bower karma gulp

mkdir ~/Downloads

#ZeroMQ
cd ~/Downloads
wget http://download.zeromq.org/zeromq-4.1.2.tar.gz
tar -zxvf zeromq-4.1.2.tar.gz
cd zeromq-4.1.2
./configure --without-libsodium --prefix=/usr/local/stow/zeromq-4.1.2
make
sudo make install
cd /usr/local/stow
sudo stow -vv zeromq-4.1.2

#Google protobuf
cd ~/Downloads
git clone https://github.com/google/protobuf.git
cd protobuf
./autogen.sh
./configure --prefix=/usr/local/stow/protobuf-`git rev-parse --short HEAD`
make
sudo make install
cd /usr/local/stow
sudo stow -vv protobuf-*

sudo ldconfig

#OpenSTF
cd
mkdir openstf
cd openstf/
git clone https://github.com/openstf/stf.git
cd stf
sudo npm install -g stf --unsafe-perm

STF の実行

さきほど書いた手順で RethinkDB を起動しておきます。
そのうえで Ubuntu上 で stf local を実行します。(どの階層で実行してもOK)
下のような実行結果になるはず。

image.png

http://localhost:7100
にアクセスして次のページが表示されれば STF の起動は成功です。

image.png

STF 上で端末を操作する

端末が認識されない場合

基本的なところの確認はこちらのページがとても詳しくてわかりやすいです。
[Android] デバッグモードで接続した端末がadb devicesで認識されない場合の対処法

Windows Subsystem in Linux 特有の確認ポイント

Windows と Ubuntu で同じバージョンの adb を起動する必要があります。
Ubuntu 上の adb のバージョンは adb --version で確認できます。

image.png

上の画面だと、1.0.39 です。
このバージョンと同じ adb を探します。
一覧のページが全く見つけられなかったのですが URL でバージョンを直接指定する方法で乗り切れます。

例えば
http://dl.google.com/android/repository/platform-tools_r23.0.1-windows.zip
 → 1.0.41
http://dl.google.com/android/repository/platform-tools_r21.0.1-windows.zip
 → 1.0.39

ダウンロードできたら解凍してインストールしてください。
インストールできたら、PowerShell で adb start-server します。

image.png

うまくいかないときは、kill-server をしてから start-server を試してみてください。
また、この操作をすると Linux 上で STF が終了するので、再度 stf local を実行して起動しなおしてください。

うまくいけば、STF 上に端末が表示されるはずです。

以上です。おつかれさまでした!

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

Windows Subsystem for Linux で OpenSTF (Smartphone Test Farm) を動かす

はじめに

OpenSTF (Smartphone Test Farm) とは

https://openstf.io/
スマートフォンやタブレットを集中管理し、ブラウザから遠隔操作できるようにするオープンソースソフトウェア。
エミュレータとは違い、物理端末のスクリーンキャプチャを転送する仕組みなので動作を正確に確認できるのが特長です。
スマホアプリやモバイル向けWebページを制作する人の味方。
image.png
動かすのに結構苦労したので、誰かの助けになればと思い記事にしていきます。

試した環境

  • Windows 10 Home 1903
  • Linux には Ubuntu 18.04.2 LTS (Bionic Beaver) を使用

Windows Subsystem for Linux (Ubuntu) の インストール

特にこだわりはなかったので STF の記事が多かった Ubuntu を選択しました。

Windows の機能「Windows Subsystem for Linux」の有効化をして
image.png

Microsoft Store から Ubuntu を探してインストールすればOK。
(バージョンの記載が無いものを選んだら 18.04 が入りました)
image.png

これでもう Ubuntu は使えます。
image.png

バージョンの確認は cat /etc/os-release でできます。
Ubuntu への貼り付けは右クリックでできます。
image.png

RethinkDB のインストール

STF ではデータベースとして RethinkDB を使用するので、先に RethinkDB を入れます。
Linux 上にインストールする方法もあるのですが Linux がわからなすぎてハマりました。
RethinkDB は Windows 上でも動くので、Linux に不慣れな方は Windows 上にインストールするのをお勧めします。
https://www.rethinkdb.com/docs/install/windows/
からダウンロードしてきて
image.png
任意のフォルダに展開し、rethinkdb.exe を起動するだけです。
(exeファイルをダブルクリックでも、コマンドプロンプトから実行でもOKです)
image.png
このように表示されれば起動中です。
http://localhost:8080 にアクセスすれば RethinkDBが動いているのを確認できると思います。
Tables や Indexes が 0 でないのは STF をインストール後にスクリーンキャプチャしたためです。
手順通り進めた場合、この段階では 0 で大丈夫です。
image.png

rethinkdb がエラーになる/ページがエラーになる場合は?

RethinkDB はポート 8080 を使用します。
他で使用していないか確認し、ある場合は他の設定を解除してから試してみてください
(色々入れてる環境で起きがち…)

STF に必要な他のアプリと、STF 本体のインストールする

RethinkDB 以外にもいくつかアプリが必要です。
これらは Linux 上にインストールしていきましょう。

こちらのお二方の記事を参考にて進めました。
http://yakisaba.hatenablog.jp/entry/2018/07/26/103620
https://qiita.com/tanaka512/items/4792931fe08b08441dc7

以下全部 Linux にコピーして貼り付けて実行、を繰り返せばOK

sudo apt-get update
sudo apt-get -y install npm
sudo apt-get -y install git android-tools-adb python autoconf automake libtool build-essential ninja-build libzmq3-dev libprotobuf-dev graphicsmagick yasm stow
#警告が出るが気にしない
sudo npm install -y -g bower karma gulp
#ZeroMQ
mkdir ~/Downloads
cd ~/Downloads
wget http://download.zeromq.org/zeromq-4.1.2.tar.gz
tar -zxvf zeromq-4.1.2.tar.gz
cd zeromq-4.1.2
./configure --without-libsodium --prefix=/usr/local/stow/zeromq-4.1.2
make
sudo make install
cd /usr/local/stow
sudo stow -vv zeromq-4.1.2
#Google protobuf 10分程度かかる
cd ~/Downloads
git clone https://github.com/google/protobuf.git
cd protobuf
./autogen.sh
./configure --prefix=/usr/local/stow/protobuf-`git rev-parse --short HEAD`
make
sudo make install
cd /usr/local/stow
sudo stow -vv protobuf-*
sudo ldconfig
#OpenSTF
cd
mkdir openstf
cd openstf/
git clone https://github.com/openstf/stf.git
cd stf
sudo npm install -g stf --unsafe-perm

STF の実行

さきほど書いた手順で RethinkDB を起動しておきます。
そのうえで Ubuntu上 で stf local を実行します。(どの階層で実行してもOK)
下のような実行結果になるはず。

image.png

http://localhost:7100
にアクセスして次のページが表示されれば STF の起動は成功です。

※最初に表示されるログイン画面はただのモックなので何をいれても進めます。

image.png

STF 上で端末を操作する

端末が認識されない場合

基本的なところの確認はこちらのページがとても詳しくてわかりやすいです。
[Android] デバッグモードで接続した端末がadb devicesで認識されない場合の対処法

Windows Subsystem in Linux 特有の確認ポイント

Windows と Ubuntu で同じバージョンの adb を起動する必要があります。
Ubuntu 上の adb のバージョンは adb --version で確認できます。

image.png

上の画面だと、1.0.39 です。
このバージョンと同じ adb を探します。
一覧のページが全く見つけられなかったのですが URL でバージョンを直接指定する方法で乗り切れます。

例えば
http://dl.google.com/android/repository/platform-tools_r23.0.1-windows.zip
 → 1.0.41
http://dl.google.com/android/repository/platform-tools_r21.0.1-windows.zip
 → 1.0.39

ダウンロードできたら解凍してインストールしてください。
インストールできたら、PowerShell で adb start-server します。

image.png

うまくいかないときは、kill-server をしてから start-server を試してみてください。
また、この操作をすると Linux 上で STF が終了するので、再度 stf local を実行して起動しなおしてください。

うまくいけば、STF 上に端末が表示されるはずです。

以上です。おつかれさまでした!

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

スライム絵文字をTextViewに設定したい!

こんばんは。
突然ですが、スライム絵文字って知ってますか?
海をわたるとBlob Emojiとか呼ばれてるやつです。

Android 7まで採用されてた黄色のかわいい絵文字のことです。
丸いやつじゃないです。

実装方法とか

  • NotoColorEmoji.ttf(Blobmoji.ttf)をTextViewに設定する。
  • C1710/FilemojiCompat つかう
    • EmojiCompatに対応したttfファイルを読み込める。簡単?
    • TypeFaceを設定していても使える?←つおい

NotoColorEmojiをただ設定すればいいわけじゃない。

まずサンプルコードです。
予めダウンロードしといてね→https://github.com/C1710/blobmoji/releases
MainActivity.ktはカットで。
やってることはTextViewをおいてfontFamilyにスライム絵文字のフォントファイルを指定しているだけです。

    <TextView
            android:layout_width="wrap_content"
            android:textSize="30sp"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="123456789\nあいうえお\n??????"/>

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="123456789\nあいうえお\n??????"
            android:textSize="30sp"
            android:padding="10dp"
            android:fontFamily="@font/no_number_blobmoji"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" android:id="@+id/textview"/>

どこにフォントファイル置くのかって話ですが、
resの中にfontを作って中に入れればいいです。
大文字は使えませんので気をつけて。
SnapCrab_NoName_2019-7-10_23-36-55_No-00.png

これだとうまくいきません。
Screenshot_20190710-233604.png

何が起きたか

なぜか数字まで絵文字で表示されてしまったので、(数字の絵文字が白色なので背景と同化して)見えなくなりました。
数字までが絵文字になっているのです。絵文字だけでいいのに。

直す

fonttools/fonttoolsを使います。
Pythonのセットアップは省きます。Pythonなんて触ったこと無いので

インストールする

pip install fonttools

数字だけ消す?

Issueありました。
https://github.com/googlefonts/noto-emoji/issues/218

この手順をたどってスライム絵文字を使えるようにしましょう。

手順

1.適当にディレクトリ(フォルダ)作成。今回はfontsってフォルダをデスクトップに置くことにします。

2.そこにBlobmoji.ttfを入れます。名前を変えて最初の文字を小文字に変えときましょう。

3.さらにテキストファイルを作ってください。名前はcodes-no-numbers.txtで行きましょう。

4.そのテキストファイルに以下のサイトの内容を入れてください。
U+00000からU+FE837まで
https://gist.github.com/MKuranowski/bdbdfb96cf87f6302cd43860aa347163

5.PowerShell(じゃなくてもいいけど)を開いて以下の通りに入力してください。
fonttoolsがちゃんと入ってれば完了するはずです。

pyftsubset blobmoji.ttf --output-file=no_number_blobmoji.ttf --unicodes-file=codes-no-numbers.txt

SnapCrab_NoName_2019-7-10_23-46-33_No-00.png

6.完成です
これをfontディレクトリに入れてfontFamilyに指定してあげればいいはずです。
SnapCrab_NoName_2019-7-11_0-48-28_No-00.png

完成

Screenshot_20190710-234722.png

:thinking:←かわいいいいいいいいいいいいいいい

私はAndroid 7時代の:thinking:が使えて満足です。

便利な使い方とか

GalaxyとかHTCとかASUSとかのAndroid端末は標準でフォント差し替え機能があります。
直接ttfファイルを入れるのではなく、FlipFont Managerを使うことで利用できます。
AOSPで実装してほしい。Root化は流石に、、

お疲れ様です

ありがとうございました。端折りすぎてよくわからんなこれ。
本当は好きなフォントと結合して使いたかったんですが、流石にできないっぽいかな。
お金かかるのでなしです(2回目

参考ページ

https://github.com/fonttools/fonttools
https://github.com/C1710/blobmoji/releases
https://github.com/googlefonts/noto-emoji/issues/218
https://gist.github.com/MKuranowski/bdbdfb96cf87f6302cd43860aa347163

おまけ

Android 10 ってIMEI取れない?
https://developer.android.com/preview/privacy/data-identifiers?hl=ja

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