AndroidのOSSライセンス表示を簡単にする『license-scribe-plugin』を開発した

はじめに
こんにちは、Androidエンジニアの@syarihuです。
Androidアプリ開発では、Apache 2.0やMITなどのOSSライブラリを多数利用します。これらのライセンスにはattribution(帰属表示)の義務があり、アプリ内でライセンス情報を表示する必要があります。Googleも開発者の責任としてOSSライセンス表示を求めています。
本記事では、Gradle 9に対応し、Edge to Edgeやダークテーマにも対応したOSSライセンス管理プラグイン「license-scribe-plugin」を紹介します。
2種類のプラグインの紹介
license-scribe-pluginは、用途に応じて2種類のプラグインを提供しています。
| プラグイン | ユースケース |
|---|---|
| license-scribe-screen-plugin | テーマカラー以外のカスタマイズ不要で、手軽にOSSライセンス画面を表示したい場合 |
| license-scribe-gradle-plugin | UIをカスタマイズしたい、ライセンス情報をGit管理して意図しないライセンス(GPLなど)の混入を検知したい場合 |
まずは、このプラグインで何ができるのか、実際の画面を見てみましょう。
実際の画面イメージ
license-scribe-screen-pluginを使うと、次のようなOSSライセンス画面を簡単に実装できます。
| Light Mode | Dark Mode |
|---|---|
![]() |
![]() |
また、license-scribe-gradle-pluginを使えば、Jetpack Composeで自由にUIをカスタマイズすることもできます。

1行でOSSライセンス画面を起動
license-scribe-screen-pluginを使えば、たった1行でOSSライセンス画面を起動できます。
OpenSourceLicensesActivity.start(context)
テーマカラーをカスタマイズしたい場合も、引数を指定するだけで対応できます。
OpenSourceLicensesActivity.start(
context = this,
themeColor = "#6200EE".toColorInt(),
)
themeColorでツールバーの背景色とアクセントカラーを指定できます。ツールバーのタイトル文字色はthemeColorの明るさに基づいて自動的に黒か白が選択されます。
開発の背景
筆者は本業のAndroidプロジェクトでlicense-list-pluginを利用していました。このプラグインは依存関係のライセンス情報をYAML形式で出力し、JSONやHTMLに変換できる便利なツールです。
しかし、Gradle 9対応に伴いlicense-list-pluginがdeprecatedになったため、代替ツールを探す必要がありました。
筆者が必要としていた機能
代替ツールを検討する中で、筆者のユースケースでは次のような機能が必要だとわかりました。
| 必要だった機能 | 理由 |
|---|---|
| Kotlinコードの自動生成 | HTMLではなくネイティブUIでライセンス画面を実装したかった |
| Edge to Edge対応 | Android 15以降で必須となるため |
| ダークテーマ対応 | アプリ全体がダークテーマに対応しているため |
| ライセンスの可視化 | 意図しないライセンス(GPLなど)の混入を検知しやすくしたかった |
既存のツールはそれぞれの設計思想に基づいて作られており、筆者のユースケースとは方向性が異なっていました。そこで、これらの機能を備えた新しいプラグインを開発することにしました。
license-scribe-pluginで実現したこと
前述の要件を満たすため、次のような特徴を持つlicense-scribe-pluginを開発しました。
- ライセンスファーストのYAML構造で見通しが良い
- Kotlinコード自動生成で変換ロジック不要
- Edge to Edge対応とダークテーマ対応
- 「カスタマイズしたい人」と「手軽に使いたい人」の両方に対応
license-scribe-screen-plugin(手軽派向け)
oss-licenses-pluginにインスパイアされて開発したプラグインです。YAML管理不要で、1行でOSSライセンス画面を起動できます。
主な特徴
- YAML管理不要
- 1行で画面起動可能
- Edge to Edge対応
- ダークテーマ対応(デフォルトでシステム設定に追従、常にオン/オフも設定可能)
- テーマカラーのカスタマイズ可能
- Composeを導入していないプロジェクトでも利用可能
設計上の工夫
生成されるOpenSourceLicensesActivityは、内部的にはComposeではなく従来のView(RecyclerView)で実装しています。
Composeで実装することも可能でしたが、Composeを導入していないプロジェクトでも手軽に使えることを優先し、意図的にViewベースで生成するようにしました。
必要なAndroidX依存関係(core-ktx, activity, appcompat, recyclerview)は、ほとんどのAndroidプロジェクトで既に含まれているため、実質的にruntime libraryを追加するだけで導入できます。
導入手順
license-scribe-pluginはMaven Centralに公開しているため、追加のリポジトリ設定は不要です。build.gradle.ktsにプラグインと依存関係を追加するだけで利用できます。
// build.gradle.kts
plugins {
id("net.syarihu.license-scribe-screen") version "<latest-version>"
}
dependencies {
// Runtime library
implementation("net.syarihu.licensescribe:license-scribe-runtime:<latest-version>")
// 以下のAndroidX依存関係は生成されるActivityに必要ですが、
// 通常のAndroidプロジェクトでは既に含まれていることがほとんどです
// implementation("androidx.core:core-ktx:...")
// implementation("androidx.activity:activity:...")
// implementation("androidx.appcompat:appcompat:...")
// implementation("androidx.recyclerview:recyclerview:...")
}
licenseScribeScreen {
generatedPackageName.set("com.example.app")
}
generatedPackageNameには、生成されるコードのパッケージ名を指定します。
使い方
設定画面のPreferenceからOSSライセンス画面を起動する例を示します。まず、Preference XMLでライセンス項目を定義します。
<!-- res/xml/preferences.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.preference.PreferenceCategory android:title="About">
<androidx.preference.Preference
android:key="open_source_licenses"
android:title="Open Source Licenses"
android:summary="View licenses for open source software" />
</androidx.preference.PreferenceCategory>
</androidx.preference.PreferenceScreen>
次に、PreferenceFragmentCompatでクリックリスナーを設定し、OSSライセンス画面を起動します。
package com.example.app
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
findPreference<Preference>("open_source_licenses")?.setOnPreferenceClickListener {
// デフォルトスタイルで起動
OpenSourceLicensesActivity.start(requireContext())
true
}
}
}
OpenSourceLicensesActivity.start()メソッドを呼び出すだけでOSSライセンス画面が起動します。テーマカラーをカスタマイズしたい場合は、次のようにthemeColorを指定します。
OpenSourceLicensesActivity.start(
context = requireContext(),
themeColor = "#6200EE".toColorInt(),
)
toolbarTitleColorは、themeColorの明るさに基づいて自動的に黒か白が選択されます。自動判定ではなく自分で色を指定したい場合は、toolbarTitleColorを明示的に設定してください。
OpenSourceLicensesActivity.start(
context = requireContext(),
themeColor = "#6200EE".toColorInt(),
toolbarTitleColor = Color.WHITE, // 明示的に白を指定
)
license-scribe-gradle-plugin(カスタマイズ派向け)
license-list-pluginにインスパイアされて開発したプラグインです。ライセンス情報をYAMLで管理し、Kotlinコードを自動生成します。
ただし、license-list-pluginとはYAMLの構造や仕組みが全く異なるため、互換性はありません。license-list-pluginから移行する場合は、新規でYAMLを生成する必要があります。
主な特徴
- ライセンスファーストのYAML構造で、プロジェクトで使用しているライセンスが一目でわかる
- Kotlinコード自動生成でUI実装が自由
- マルチモジュール対応
- appモジュールが依存するモジュールの依存関係も推移的に取得するため、必要なライセンスを正しく取得可能
- 生成されるコードは
LicenseProviderインターフェースを実装しているため、DIで扱いやすい
導入手順
build.gradle.ktsにプラグインと依存関係を追加します。
// build.gradle.kts
plugins {
id("net.syarihu.license-scribe") version "<latest-version>"
}
dependencies {
implementation("net.syarihu.licensescribe:license-scribe-runtime:<latest-version>")
}
licenseScribe {
baseDir.set(layout.projectDirectory.dir("licenses"))
generatedPackageName.set("com.example.app")
}
baseDirにはライセンス定義ファイルを配置するディレクトリを、generatedPackageNameには生成されるコードのパッケージ名を指定します。
初期設定
プラグインを追加したら、初期化タスクを実行してYAMLファイルを生成します。
./gradlew scribeLicensesDebugInit
このコマンドを実行すると、licenses/debug/scribe-licenses.ymlが生成されます。
タスク名のDebug部分はビルドバリアントに応じて変わります。Releaseバリアントの場合はscribeLicensesReleaseInit、Product Flavorsを使用している場合はscribeLicensesStagingDebugInitのようにフレーバー名とビルドタイプを組み合わせた名前になります。以降のタスク(Sync、Generate、Reportなど)も同様です。
YAMLファイルの出力先もバリアントごとに異なります(licenses/debug/、licenses/release/など)。複数のビルドバリアントを使用している場合は、それぞれのYAMLファイルをGitに含める必要があります。
YAMLの構造
生成されるYAMLはライセンスファーストの構造になっています。次に生成されるYAMLの例を示します。
licenses:
apache-2.0:
name: Apache License 2.0
url: https://www.apache.org/licenses/LICENSE-2.0
artifacts:
com.squareup.okhttp3:
- name: okhttp
url: https://square.github.io/okhttp/
copyrightHolders:
- Square, Inc.
com.squareup.retrofit2:
- name: retrofit
url: https://github.com/square/retrofit
copyrightHolders:
- Square, Inc.
mit:
name: MIT License
url: https://opensource.org/licenses/MIT
artifacts:
org.checkerframework:
- name: checker-qual
url: https://checkerframework.org
copyrightHolders:
- Michael Ernst
トップレベルにライセンスの種類(apache-2.0、mitなど)があり、その下にそのライセンスを使用しているアーティファクトがグループ化されています。これにより、プロジェクトで使用しているライセンスの種類が一目でわかります。
YAMLの編集
POMにライセンス情報や著作権者情報が含まれていない場合、ライセンスはunknownとして、copyrightHoldersは空の状態で生成されます。これらの情報は手動で編集して正しい値を設定する必要があります。
licenses:
unknown:
name: Unknown License
url: null
artifacts:
com.example:
- name: some-library
copyrightHolders: [] # 著作権者を手動で追加
上記のようなunknownライセンスのアーティファクトは、正しいライセンス(apache-2.0やmitなど)の下に移動し、copyrightHoldersを追記してください。
依存関係の更新
依存関係を追加・削除した場合は、Syncタスクを実行してYAMLを更新します。
./gradlew scribeLicensesDebugSync
Syncを実行すると、新しい依存関係が追加され、削除された依存関係が取り除かれます。手動で編集したライセンス情報やcopyrightHoldersなどの情報は引き継がれるため、一度編集した内容が失われることはありません。
KotlinコードはassembleDebug時に自動生成されるため、YAMLの更新だけで完了します。
Composeでの利用例
生成されたKotlinコードを使って、Jetpack ComposeでOSSライセンス画面を実装する例を示します。
package com.example.app.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp
import com.example.app.AppLicenses
import net.syarihu.licensescribe.LicenseInfo
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun OpenSourceLicensesScreen() {
val uriHandler = LocalUriHandler.current
Scaffold(
topBar = {
TopAppBar(title = { Text("Open Source Licenses") })
},
) { innerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
contentPadding = PaddingValues(vertical = 8.dp),
) {
items(AppLicenses.all, key = { it.artifactId }) { license ->
LicenseItem(
license = license,
onItemClick = {
license.artifactUrl?.let { uriHandler.openUri(it) }
},
onLicenseClick = {
license.licenseUrl?.let { uriHandler.openUri(it) }
},
)
}
}
}
}
@Composable
private fun LicenseItem(
license: LicenseInfo,
onItemClick: () -> Unit,
onLicenseClick: () -> Unit,
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.clickable(onClick = onItemClick),
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = license.artifactName,
style = MaterialTheme.typography.titleMedium,
)
Text(
text = license.artifactId,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
if (license.copyrightHolders.isNotEmpty()) {
Text(
text = license.copyrightHolders.joinToString(", "),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
Text(
text = license.licenseName,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(top = 8.dp)
.clickable(
enabled = license.licenseUrl != null,
onClick = onLicenseClick,
),
)
}
}
}
AppLicenses.allで全てのライセンス情報を取得し、LazyColumnで一覧表示しています。LicenseInfoにはartifactName、artifactId、copyrightHolders、licenseName、licenseUrlなどの情報が含まれており、自由にUIを構築できます。
license-scribe-hilt-plugin(オプション)
マルチモジュールプロジェクトでHiltを利用している場合に便利なオプションプラグインです。
ユースケース
- マルチモジュールプロジェクトでHiltを利用している
- featureモジュール(例: 設定画面)からOSSライセンス画面を起動したい
導入手順
appモジュールのbuild.gradle.ktsにプラグインを追加します。
// app/build.gradle.kts
plugins {
id("net.syarihu.license-scribe") version "<latest-version>"
id("net.syarihu.license-scribe-hilt") version "<latest-version>"
}
これにより、次のようなHiltモジュールが自動生成されます。
@Module
@InstallIn(SingletonComponent::class)
object LicenseScribeHiltModule {
@Provides
@Singleton
fun provideLicenseProvider(): LicenseProvider = AppLicenses
}
featureモジュールでの利用
featureモジュールでは、runtime libraryのみを依存関係に追加します。
// feature/settings/build.gradle.kts
dependencies {
implementation("net.syarihu.licensescribe:license-scribe-runtime:<latest-version>")
}
ViewModelでは、LicenseProviderインターフェースを通じてライセンス情報を取得できます。
@HiltViewModel
class LicenseViewModel @Inject constructor(
private val licenseProvider: LicenseProvider
) : ViewModel() {
val licenses: List<LicenseInfo> = licenseProvider.all
}
LicenseProviderはインターフェースなので、Hilt以外のDIフレームワークを使っている場合でも、生成されたAppLicensesをDIコンテナに登録すれば同様に利用できます。
便利機能の紹介
CIでのライセンスチェック
CIでライセンスの差分チェックを自動化したい場合は、Checkタスクを使用します。
./gradlew scribeLicensesDebugCheck
このタスクは、YAMLに未記録の依存関係(Missing)や削除済みの依存関係(Removed)が存在する場合にエラーで終了します。これにより、依存関係の追加・削除時にYAMLの更新漏れをCIで検出できます。
HTML Diff Report機能
license-scribe-gradle-pluginには、依存関係の変更を確認できるHTML Diff Report機能があります。リリース前に依存関係の漏れがないか確認したい場合に便利です。
./gradlew scribeLicensesDebugReport
このコマンドを実行すると、次のようなレポートが生成されます。

レポートでは、依存関係のステータスが色分けで表示されます。
- Covered: YAMLに記録済みの依存関係
- Missing: プロジェクトに存在するがYAMLに未記録の依存関係
- Removed: YAMLに記録されているがプロジェクトから削除された依存関係
まとめ
license-scribe-pluginは、用途に応じたプラグインを提供しています。
| 要件 | おすすめプラグイン |
|---|---|
| 手軽にOSSライセンス画面を表示したい | license-scribe-screen-plugin |
| UIをカスタマイズしたい、ライセンス情報をGit管理して意図しないライセンスの混入を検知したい | license-scribe-gradle-plugin |
| マルチモジュール + Hilt | license-scribe-gradle-plugin + license-scribe-hilt-plugin |
ぜひ、プロジェクトの要件に合わせて活用してみてください。


Discussion