Google I/O 2018 にみる Android KTX その1

Android KTX は、次期 androidx のパッケージとも深い関わりをもっているようです。

androidx: Hello World!
You may notice that Android KTX uses package names that begin with androidx. This is a new package name prefix that we will be using in future versions of Android Support Library. We hope the division between android.* and androidx.* makes it more obvious which APIs are bundled with the platform, and which are static libraries for app developers that work across different versions of Android.

Android Developers Blog: Introducing Android KTX: Even Sweeter Kotlin Development for Android

また、パッケージ名に関してのマッピングも公式からアナウンスされ始めています。

AndroidX refactoring  |  Android Developers

そんな Android KTX に関係するいくつかの動画がアップされています。

Android Jetpack: sweetening Kotlin development with Android KTX (Google I/O 2018) - YouTube

Google I/O 2018: Stage 2 - YouTube

前回は、よく分からんオネーチャンが喋ってましたが、 今回は、Jake Wharton さんが遂に表に登場しました。

彼のトークやスライドは開発に有効に利用できることばかりです。

Kotlin の Extension Function の勉強にもなりますので、登場したコードを書き出しておきます。

並び順は、

「よくあるコード」(before)
「Android KTX のコード(拡張関数)」(KTX)
「KTXを利用したコード」(after)

です。

 

ViewGroupの再帰処理


// before
val userLayout: ViewGroup = findViewById(R.id.users)
for (index in 0 until userLayout.childCount) {
  val view = userLayout.getChildAt(index)
  // Do something with index and view...
}


// KTX
fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) {
  for (index in 0 until childCount) {
    action(index, getChildAt(index))
  }
}


// after
val userLayout: ViewGroup = findViewById(R.id.users)
userLayout.forEachIndexed { index, view ->
  // Do something with index and view...
}

 

getSystemService()


// before

// In an Activity, on API 23+...
val notifications = getSystemService(NotificationManager::class.java)

// or all API levels...
val notifications = ContextCompat.getSystemService(this,
  NotificationManager::class.java)


// KTX
inline fun <reified T> Context.systemService() =
  ContextCompat.getSystemService(this, T::class.java)


// after
val notifications = systemService<NotificationManager>()

 

Viewのパディング


// before
avatarView.setPadding(
  10, avatarView.paddingTop, 10, avatarView.paddingBottom)


// KTX
inline fun View.updatePadding(
  left: Int = paddingLeft,
  top: Int = paddingTop,
  right: Int = paddingRight,
  bottom: Int = paddingBottom
) {
  setPaddinng(left, top, right, bottom)
}


// after
avatarView.updatePadding(left = 10, right = 10)

 

デストラクチャ


// before
val rect = avaterView.clipBounds
val left = rect.left
val top = rect.top
val right = rect.right
val bottom = rect.bottom
// Use left, top, right, bottom...


// KTX
inline operator fun Rect.component1() = left
inline operator fun Rect.component2() = top
inline operator fun Rect.component3() = right
inline operator fun Rect.component3() = bottom


// after
val (left, top, right, bottom) = avatarView.clipBounds
// Use left, top, right, bottom...

val (left, top, right) = avatarView.clipBounds
// Use left, top, right...

val (left, _, right) = avatarView.clipBounds
// Use left, right...

 

すべての値のチェック


// before
val onlyDigits = true
for (c in phoneNumber) {
  if (!c.isDigit()) {
    onlyDigits = false
    break
  }
}


// before
val onlyDigits = phoneNumber.all { it.isDigit() }


// before
val onlyDigits = TextUtils.isDigitsOnly(phoneNumber)


// KTX
inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)


// after
val onlyDigits = phoneNumber.isDigitsOnly()

とりあえずは、前半のコードを書き出しておいて、次回へ

(つづく...)


【Android Oreo】「Picture in Picture」は進化中?!

サンプル見てましたが。

android-PictureInPicture/README.md at master · googlesamples/android-PictureInPicture

...。

ただ、縮小されたActivityを常駐的に端末画面の上に載せたいだけなのですが...。

 

最小限の実装

BlankActivityで実装するだけ。

これぐらいまで。

AndroidManifest.xml


<activity android:name=".MainActivity"
    android:resizeableActivity="true"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">

MainActivity.kt


class MainActivity : AppCompatActivity() {

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

    if (VERSION.SDK_INT >= VERSION_CODES.O) {
      text.setOnClickListener {
        val params = PictureInPictureParams.Builder().apply {
          setAspectRatio(Rational(4, 3))
        }.build()
        enterPictureInPictureMode(params)
      }
    }
  }

  override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
      newConfig: Configuration) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
    if (isInPictureInPictureMode) {
      text.text = "small"
      setFullScreen(true)
    } else {
      text.text = "normal"
      setFullScreen(false)
    }
  }

  private fun setFullScreen(on: Boolean) {
    window.decorView.systemUiVisibility = if (on) {
      SYSTEM_UI_FLAG_LAYOUT_STABLE or
      SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
      SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
      SYSTEM_UI_FLAG_HIDE_NAVIGATION or
      SYSTEM_UI_FLAG_FULLSCREEN or
      SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    } else {
      SYSTEM_UI_FLAG_LAYOUT_STABLE
    }
  }

}

PinPモードに入ったときのレイアウト変更は、フルスクリーンモードでツールバーを隠すのみ。

Preview 公開当時のサンプルが多いので、今現在の最小限実装でやってみたメモ的な。

関連APIは変化している様子。


Gradle Dependency バージョンのチェックを「最新」に更新する方法

便利そうなのでました!

Android Developers Blog: Announcing new SDK versioning in Google Play services and Firebase

com.google.android.gms:play-services-*
com.google.firebase:firebase-*

に関しては、それぞれが独立したバージョンで記述してよい。

なので、以下のような「バージョンをを揃える」記述は不要。


buildscript {
    ext {
        play_version = '15.0.0'
    }
}

dependencies {
    // DON'T DO THIS!!
    // The following use of the above buildscript property is no longer valid.
    implementation "com.google.android.gms:play-services-auth:${play_version}"
    implementation "com.google.firebase:firebase-auth:${play_version}"
    implementation "com.google.firebase:firebase-firestore:${play_version}"
}

利用方法は以下2パターン。


classpath 'com.google.gms:google-services:3.3.0'

// 最終行
apply plugin: 'com.google.gms.google-services' 

かまたは、


classpath 'com.google.android.gms:strict-version-matcher-plugin:1.0.0'

// 最終行
apply plugin: 'com.google.android.gms.strict-version-matcher-plugin'

とのこと。

 

やってみたが...

エラー。

ビルドできず。


The library com.google.android.gms:play-services-measurement-base
is being requested by various other libraries at [[15.0.0,15.0.0], [15.0.2,15.0.2]],
but resolves to 15.0.2. Disable the plugin and check your dependencies tree
using ./gradlew :app:dependencies.

記述にはない「play-services-measurement-base」が

内部的に呼ばれてこけている。

メッセージに書いてある


./gradlew :app:dependencies

は実行すらできない。

それぞれは、Andrid Studioの自動チェックで最新のはずなのだが。

 

それぞれのバージョンを確認してみる

目視でGoogleリポジトリを確認。

Google's Maven Repository

AndroidStudioの自動チェックで最新版の「15.0.0」だと思っていたが

実は、古いバージョンのままだった。

実際は「15.0.2」が最新。

これに書き換えたらいけた。

しかし、AndroidStudioの自動最新バージョンチェック機能らしきは、

機能してなくね?

かまたは、微妙に古くね?

 

Lint「Newer Library Versions Available」 を確認する

「Preference」-「Editor」-「Inspections」 にある

「Lint」-「Newer Library Versions Available」

OFFになってるが。

Description
Newer Library Versions Available This detector checks with a central repository to see if there are newer versions available for the dependencies used by this project. This is similar to the GradleDependency check, which checks for newer versions available in the Android SDK tools and libraries, but this works with any MavenCentral dependency, and connects to the library every time, which makes it more flexible but also much slower.

「毎回チェックするのでとろい」ということで

デフォルトでOFFになっているのか。

毎回でなく適時、意図的に実行したい場合は、

「Analyze」-「Run Inspection by Name」からいきましょう。

 

グレーのハイライトと同時にツールチップで最新版である

「15.0.2」

をサジェストしてくれました!

【Android Studio】ビルド環境を安定した最新バージョンにする