com.google.gms:oss-licenses を使ってオープンソースライセンスを表示する

ライブラリのライセンス管理・表示を簡単に行えるツールが Google Play サービス 11.2.0 から含まれるようになり、とりあえず表示するだけなら非常に簡単だったので、導入方法と注意点を残します。

https://developers.google.com/android/guides/opensource

## 導入

Android Studio 3.0 で新規作成したプロジェクトに対して導入してみます。

導入は非常に簡単です。

依存しているライブラリの pom からライセンス情報を取得するための Gradle プラグイン `com.google.gms.oss.licenses.plugin` と、ライセンス表記を行う Activity を提供するライブラリ `play-services-oss-licenses` を dependencies に追加します。

### build.gradle

```diff
buildscript {
    ext.kotlin_version = '1.1.51'
    repositories {
+       google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+       classpath 'com.google.gms:oss-licenses:0.9.1'
    }
}
```

### app/build.gradle

```diff
  apply plugin: 'com.android.application'
  apply plugin: 'kotlin-android'
  apply plugin: 'kotlin-android-extensions'
+ apply plugin: 'com.google.gms.oss.licenses.plugin'

  android {
      compileSdkVersion 26
      defaultConfig {
          applicationId "com.github.sho5nn.sample"
          minSdkVersion 23
          targetSdkVersion 26
          versionCode 1
          versionName "1.0"
          testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
      }
      buildTypes {
          release {
              minifyEnabled false
              proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
          }
      }
  }

  dependencies {
      implementation fileTree(dir: 'libs', include: ['*.jar'])
      implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
      implementation 'com.android.support:appcompat-v7:26.1.0'
      implementation 'com.android.support.constraint:constraint-layout:1.0.2'
      testImplementation 'junit:junit:4.12'
      androidTestImplementation 'com.android.support.test:runner:1.0.1'
      androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
+     implementation 'com.google.android.gms:play-services-oss-licenses:11.6.0'
  }
```

この状態でビルドすると `app/src/main/res/raw` ディレクトリに、プロジェクトが依存しているライブラリのライセンス情報をまとめたファイルが 2 つ生成されます。

```
// third_party_license_metadata

0:46 appcompat-v7
47:47 play-services-oss-licenses
95:46 animated-vector-drawable
142:46 support-vector-drawable
189:47 play-services-basement
237:46 support-v4
284:46 support-fragment
331:46 support-core-ui
378:46 support-media-compat
425:46 support-core-utils
472:46 support-compat
519:46 support-annotations
566:731 UTF
1298:2500 zlib
3799:19442 ICU4C
23242:11358 CCTZ
34601:680 STL
35282:1602 JSR 305
36885:1732 Protobuf Nano
38618:1481 darts_clone
40100:243 tz database
40344:1558 RE2
41903:3182 PCRE
45086:11358 safeparcel
56445:46 annotations
56492:46 runtime
56586:46 common
```

```
// third_party_licenses

http://www.apache.org/licenses/LICENSE-2.0.txt
https://developer.android.com/studio/terms.html
http://www.apache.org/licenses/LICENSE-2.0.txt
http://www.apache.org/licenses/LICENSE-2.0.txt
https://developer.android.com/studio/terms.html
http://www.apache.org/licenses/LICENSE-2.0.txt
http://www.apache.org/licenses/LICENSE-2.0.txt
http://www.apache.org/licenses/LICENSE-2.0.txt
http://www.apache.org/licenses/LICENSE-2.0.txt

...
```

次に、ライセンス表記を行ってくれる Activity を呼び出すコードを書きます。

```kotlin
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity

val intent = Intent(this, OssLicensesMenuActivity::class.java)
intent.putExtra("title", "おーぷんそーすらいせんす")
startActivity(intent)
```

これだけで、ライセンス一覧画面を表示することができます。ライセンス一覧画面からはライセンス詳細画面に遷移することができ、そこでライセンスを確認することができます。
ライセンス一覧画面の ActionBar のタイトルは `Intent#putExtra` に `title` のキー名で変更可能です。

![3.gif](https://qiita-image-store.s3.amazonaws.com/0/35834/44dfe82e-e67b-f359-15d8-0b9dce85e7a1.gif)


## 注意点

既にアプリで定義している Theme が `Theme.AppCompat.Light.NoActionBar` など ActionBar がないものだった場合、ライセンス一覧画面から遷移する詳細画面を表示するときにクラッシュしてしまいます。これは `play-services-oss-licenses` が `ActionBar#setTitle` でライブラリ名を設定しようとしているためです。

```
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference
   at com.google.android.gms.oss.licenses.OssLicensesActivity.onCreate(Unknown Source:30)
   at android.app.Activity.performCreate(Activity.java:6975)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
   at android.app.ActivityThread.-wrap11(Unknown Source:0) 
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
   at android.os.Handler.dispatchMessage(Handler.java:105) 
   at android.os.Looper.loop(Looper.java:164) 
   at android.app.ActivityThread.main(ActivityThread.java:6541) 
   at java.lang.reflect.Method.invoke(Native Method) 
   at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
```

回避方法としては、ライセンス詳細画面である `OssLicensesActivity` の Theme を、 ActionBar ありの Theme で上書きしてしまえばよいです。ライセンス一覧画面の `OssLicensesMenuActivity` も上書きできるので、ちょっとしたカスタマイズはできそう。

```diff
<manifest>
  <application>
    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
+     <activity
+       android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
+       android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
+       />
+     <activity
+       android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
+       android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
+       />
  </application>
</manifest>
```

---

以上です。