Flutterで利用しているOSSライセンス一覧を生成するDart CLIを作ってみた

せっかく作ったのですが、誰にも使われていないのでその紹介をします。
英語で書いた内容を日本語に訳しただけです。

紹介するのは、OSSライセンス一覧を生成するあれです。
Dart部分だけです。ネイティブのツールの併用して使う想定です。

iosでは、Settings.bundleを利用しています。
androidでは、[AboutLibraries](https://github.com/mikepenz/AboutLibraries)を利用しています。

どちらも他の形式でも生成できるようには考えて作っているので、他の形式に対応したプルリクエストを頂けるとありがたいです。

ソースはこちらです。
https://github.com/ko2ic/dart_oss_licenses

# インストール

まずは、dartを入れます。

```
$ brew tap dart-lang/dart
$ brew install dart
```

以下でインストール完了です。

```
$ pub global activate dart_oss_licenses
```

# 利用方法

プロジェクトのルートで以下のコマンドを叩くだけです。が、プラットフォームごとに準備が必要です。(それぞれ別段を参照してください。)

```
$ dart-oss-licenses
```

仕組みは、

1. ```pubspec.lock``` から利用しているPackageを取得
2. https://pub.dartlang.org からそれぞれのライブラリ情報を取得
3. それぞれのライブラリのライセンスを取得
4. それぞれのネイティブに合わせた形で出力

通信処理では、内容をキャッシュしてないので、何度もやると相手のサーバーに負担がかかりますので、気をつけてください。
キャッシュは早いうちに実装したいと思います。

# ios

## 準備

XcodeでSettings.bundleを作る必要があります。

File -> New -> File... -> Select Settings.bundle -> Next -> Create as <Project>/ios/Runner/Settings.bundle

Root.plistを以下に書き換えます。

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PreferenceSpecifiers</key>
	<array>
		<dict>
			<key>Type</key>
			<string>PSChildPaneSpecifier</string>
			<key>Title</key>
			<string>Licenses</string>
			<key>File</key>
			<string>com.ko2ic.dart-oss-licenses</string>
		</dict>
	</array>
	<key>StringsTable</key>
	<string>Root</string>
</dict>
</plist>
```

```en.lproj/Root.strings``` や ```ja.lproj/Root.strings``` を書き換えます。

```
"Licenses" = "Licenses";
```

コマンドを打つと```<Project>/ios/Runner/Settings.bundle/com.ko2ic.dart-oss-licenses.plist``` にファイルが生成されます。


## 実装

Androidと同じ遷移にしたいので、iosのアプリ設定画面だけにしないで、アプリからそこへ遷移できるようにしておきます。

```AppDelegate.swift
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    private var _result: FlutterResult?

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
    ) -> Bool {
        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: "sample/platform",
                                                 binaryMessenger: controller)
        channel.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            switch call.method {
            case "toOssLicense":
                if let url = URL(string: UIApplication.openSettingsURLString) {
                    if #available(iOS 10.0, *) {
                        UIApplication.shared.open(url, options: [:], completionHandler: nil)
                    } else {
                        UIApplication.shared.openURL(url)
                    }
                }
            default:
                result(FlutterMethodNotImplemented)
                break
            }
        })
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
```

その他、ネイティブで利用されているライセンスも表示したいので、[LicensePlist](https://github.com/mono0926/LicensePlist)などで生成したplistを自分でマージする必要があります。

# Android

コマンドを打つと ```/android/app/src/main/res/values/license_strings.xml``` が生成されます。
すでにファイルがある場合は、 ```/android/app/src/main/res/values/license_strings.xml.temp``` が生成されます。

取得できなかったデータは、生成されたlicense_strings.xmlで、TODOの文字列が挿入されています。
それらを自分で設定し直す必要があります。

## 実装

[AboutLibraries](https://github.com/mikepenz/AboutLibraries)の以下のバージョンを利用します。

```android/app/build.gradle
dependencies {
    implementation "com.mikepenz:aboutlibraries:6.1.1"
```

AboutLibrariesを使って、ライセンス一覧表示画面を作ります。

```MainActivity.kt
class MainActivity() : FlutterActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GeneratedPluginRegistrant.registerWith(this)

        MethodChannel(
            flutterView,
            "sample/platform"
        ).setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
            when (call.method) {
                "toOssLicense" -> {
                    LibsBuilder()
                        .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
                        .withActivityTitle("Licenses")
                        .withLicenseShown(true)
                        .withLibraries(
                            <package name>
                        )
                        //start the activity
                        .start(this)
                }
            }
        }
    }
}
```

```<package name>``` のところには、コマンド実行でコンソールで表示される```Succeeded Package List```の部分を入れてください。

```
$ dart-oss-licenses
・・・
Succeeded Package List:
"web_socket_channel","device_info","redux","sentry","image_picker","cupertino_icons","qr","mime",
```

# Dartの実装

OSSライセンス一覧に遷移したい場所で以下を呼び出します。

```
  static const platform = const MethodChannel('sample/platform');

  Future<void> toOssLicense() {
    return platform.invokeMethod('toOssLicense', {});
  }
```

# 結果

![android.gif](https://qiita-image-store.s3.amazonaws.com/0/10494/85ea55cd-da7e-88f8-d035-d0d6bc17b60f.gif) 　　<img src="https://qiita-image-store.s3.amazonaws.com/0/10494/79eb70a5-79c0-a89d-8a5b-4253fbcb89f0.gif" height="640" width="330" />