オードリー・タン氏のPRにみる言語選択肢の表示文字

これ。

👉 東京都のコロナ対策サイト、台湾の“天才IT大臣”も改善に参加 オープンソースの取り組み、「胸アツ展開」と話題 - ITmedia NEWS 

👉 Fix language selector label for zh-TW (体 -> 體) by audreyt · Pull Request #827 · tokyo-metropolitan-gov/covid19 

❌ 繁体
⭕ 繁體

ということになります。

あれ、

「繁体」

のほうをよく見かけるような気がしませんか??

本当に「繁体」は間違い?

繁体字(はんたいじ、繁體字、拼音: fántǐzì)

特に中華人民共和国の一連の「文字改革」政策による簡体字(簡化字)との対比によりこう呼ぶ。現在では主に台湾のほか、中華人民共和国の特別行政区である香港・マカオで使用され、中華圏外の華人コミュニティーでも見られる。

👉 繁体字 - Wikipedia 

Google 翻訳のWEB版とアプリ版の選択肢をみてみます。

そうか、

サイトやアプリの「表示している言語」の設定で「言語の選択肢」の表示文字が変わるのですね!

👉 Google Language Codes - tomihasa 

4つの言語に関して、表示されるべき選択肢の文字を見ておきましょう。

hl=ja

英語
中国語(簡体)
中国語(繁体)
日本語

hl=en

English
Chinese (Simplified)
Chinese (Traditional)
Japanese

hl=zh_CN

英语
中文(简体)
中文(繁体)
日语

hl=zh_TW

英文
中文(簡體)
中文(繁體)
日文

オードリー・タン氏は、台湾の方なので、台湾向け言語表示としては

「中文(繁體)」

が選択肢に表示されるべき文字となるのでしょうか。

しかし、東京都のように選択肢の文字自体を表示言語に合わせて翻訳してないサイトもあります。

👉 都内の最新感染動向 | 東京都 新型コロナウイルス感染症対策サイト 

Android Pie 言語設定画面。

Android端末では、以前からこのような実装になっています。

フォントの事情がありますので、端末や文字によっては豆腐や文字化けなどに注意が必要です。

まとめ

呼称や表記でもめたりするネット上で

オードリー・タン氏の台湾愛を見たような気がします。


【MVVM】Flow vs LiveData

👉 Using LiveData & Flow in MVVM — Part I - ProAndroidDev 

Kotlin Flow の登場で盛り上がってきました。

どれにします? どの流れにします?

Repository

Result を返す。

Flow<Result>を返す。

ViewModel

Result を受けて、LiveData<Result> を渡す。

Flow<Result> を受けて、LiveData<Result> を渡す。


Fragment

LiveData<Result> を受け取る。

Flow<Result> を受け取る。


override fun onActivityCreated(savedInstanceState: Bundle?) {
  super.onActivityCreated(savedInstanceState)

  viewModel = ViewModelProviders.of(
      this,
      viewModelFactory
  ).get(WeatherForecastDataStreamFlowViewModel::class.java)

  // Consume data when fragment is started
  lifecycleScope.launchWhenStarted {

    // Since collect is a suspend function it needs to be called
    // from a coroutine scope
    viewModel.weatherForecast.collect {
      when (it) {
        Result.Loading -> {
          Toast.makeText(context, "Loading", Toast.LENGTH_SHORT).show()
        }
        is Result.Success -> {
          tvDegree.text = it.data.toString()
        }
        Result.Error -> {
          Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show()
        }
      }
    }
  }
}

WeatherForecastDataStreamFlowFragment #L47-L75

まとめ

とはいえ、今はまだ、完全に LiveData は捨てれんよの。

👉 Kotlin で Result 
👉 Kotlin Flow vs Android LiveData - Stack Overflow 
👉 From RxJava 2 to Kotlin Flow: Threading - ProAndroidDev 

追記: ホットな Flow が登場したので以下。

👉 【MVVM】 Kotlin Flow で使える5つの利用パターン 


「Kotlinx Json」の登場でサードパーティJSONライブラリは不要となる。

JSONのライブラリ何を使ってますか?

👉 square/moshi: A modern JSON library for Kotlin and Java. 
👉 FasterXML/jackson: Main Portal page for the Jackson project 
👉 google/gson: A Java serialization/deserialization library to convert Java Objects into JSON and back 

これらは、Javaベースで書かれています。Kotlin は100%相互運用可能ですが微妙に期待しない挙動をします。


data class User(
    val name: String,
    val email: String,
    val age: Int = 13,
    val role: Role = Role.Viewer
)

enum class Role { Viewer, Editor, Owner }


{
    "name" : "John Doe",
    "email" : "[email protected]"
}


class JsonUnitTest {

    private val jsonString = """
            {
                "name" : "John Doe",
                "email" : "[email protected]"
            }
        """.trimIndent()

    @Test
    fun gsonTest() {
        val user = Gson().fromJson(jsonString, User::class.java)

        assertEquals("John Doe",user.name)
        assertEquals(null, user.role)
        assertEquals(0, user.age)

//      User(name=John Doe, 
//           [email protected], 
//           age=0, 
//           role=null)

    }

}

デフォルト値が期待通りにパースできません。

kotlinx.serialization が登場!!

JetBrains産です。間違いないでしょう。



👉 Kotlin/kotlinx.serialization: Kotlin multiplatform / multi-format serialization 

- クロスプラットフォーム
- 非リフレクション
- アノテーション @Serializable
- Kotlin v1.3.30+


@Serializable
data class User(
    val name: String,
    val email: String,
    val age: Int = 13,
    val role: Role = Role.Viewer
)

enum class Role { Viewer, Editor, Owner }

class JsonUnitTest {

    private val jsonString = """
            {
                "name" : "John Doe",
                "email" : "[email protected]"
            }
        """.trimIndent()

    @Test
    fun jsonTest() {
        val user = Json.parse(User.serializer(), jsonString)

        assertEquals("John Doe", user.name)
        assertEquals(Role.Viewer, user.role)
        assertEquals(13, user.age)

//      User(name=John Doe, 
//           [email protected], 
//           age=13, 
//           role=Viewer)

    }
}

Gson では無視されていたデフォルト値がきちんと使用されます。

以下のセットアップでどうぞ。


buildscript {
    ext.kotlin_version = '1.3.60'
    repositories { jcenter() }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    }
}


apply plugin: 'kotlin' 
apply plugin: 'kotlinx-serialization'


repositories {
    jcenter()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" // JVM dependency
}

👉 Kotlinx Json vs Gson - Juraj Kušnier - Medium 



今どきの Retrofit と LiveData で Coroutine

ありがとうございます。


👉 Retrofit 

ご存知の通り Retrofit2 では、サスペンドな関数も利用できるようになっております。

👉 SpaceX REST API で試す Retrofit の coroutine 対応 


// NewModel.kt
@GET("/feed/here/")
suspend fun getData(@Query("token") token : String) : Status


// NewRepository.kt
class Repository {
  var client = RetrofitService.createService(JsonApi::class.java)
  suspend fun getData(token : String) = client.getData(token)
}

ので、以下のようなこれまでのコードは、


// OldViewModel.kt
val data = MutableLiveData<Status>()

private fun loadData(token: String){
  viewModelScope.launch {
    val retrievedData = withContext(Dispatchers.IO) {
      repository.getData(token)
    }
    data.value = retrievedData
  }
}

シンプルに以下のように書けます。


// NewViewModel.kt
val data : LiveData<Status> = liveData(Dispatchers.IO) {
      val retrievedData = repository.getData(token)
      emit(retrievedData)
    }

ありがとうございます。

👉 Exploring new Coroutines and Lifecycle Architectural Components integration on Android 
👉 Using Retrofit 2 with Kotlin coroutines - ProAndroidDev 


Flipper で SQLite テーブルを直接見る

Stetho の Facebook が作成した Android / iOS どちらも使えるデバッグツールです。

👉 Open-sourcing Flipper: a new extensible debugging tool - Facebook Engineering 

👉 Flipper · Extensible mobile app debugging 

👉 facebook/flipper: A desktop debugging platform for mobile developers. 

We wanted to create a platform that gives us all the flexibility we need to build more advanced features and support for iOS. One of Flipper's core concepts is its extensibility using plugins. Plugins are written in React and we provide a set of ready-to-use UI components that allow developers to build great plugin UIs with a few lines of code.

iOSのより高度な機能とサポートを構築するために必要なすべての柔軟性を提供するプラットフォームを作成したかったのです。 Flipperのコアコンセプトの1つは、プラグインを使用した拡張性です。プラグインはReactで記述されており、開発者が数行のコードで優れたプラグインUIを構築できる、すぐに使用できるUIコンポーネントのセットを提供します。

👉 Stetho Guidance · Flipper 

dependencies、Application 内に記述しながら、セットアップ。


<activity android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
        android:exported="true"/>


repositories {
  jcenter()
}

dependencies {
  debugImplementation 'com.facebook.flipper:flipper:0.26.0'
  debugImplementation 'com.facebook.soloader:soloader:0.5.1'
  releaseImplementation 'com.facebook.flipper:flipper-noop:0.26.0'
}


public class MyApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, false);

    if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {
      final FlipperClient client = AndroidFlipperClient.getInstance(this);
      client.addPlugin(new InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()));
      client.start();
    }
  }
}

👉 Getting Started · Flipper 

ここでは Database プラグインを追加します。


client.addPlugin(new DatabasesFlipperPlugin(context));

👉 Databases Plugin Setup · Flipper 

あとは、PC上のデバッグクライアントを開くと直感的に参照できるようになります。

👉 android/architecture-samples at dagger-android 

使いやすいです。動作も軽快です。