Cordova 9.0にバージョンアップする際にハマったこと

Androidは新規は8月、更新は11月からAPI レベル 28に対応する必要がありますが、それに伴ってCordova 9.0(Cordova Android 8.0)にバージョンアップして色々とハマったのでまとめます。

他に何かあったら随時追記します！

## ビルド時に「Cordova error: Using “requireCordovaModule” to load non-cordova module “q” is not supported」が発生する

Promiseを使用するため、以下の様な記述をしていたが、requireCordovaModuleが削除され、Cordovaに付属するライブラリが参照できない様になっていた。

```js
var q = context.requireCordovaModule('q');
var glob = context.requireCordovaModule('glob');
```

代わりにJS標準のPromiseを使用するか、npm installして使用する。

## Adaptive Iconの設定が無視されている
今まではconfig.xmlでresource-fileを駆使して対応していたが、Cordova 9.0からは標準で対応されたため、標準機能を使用して対応。
https://cordova.apache.org/docs/en/9.x/config_ref/images.html

例

```xml
<icon src="/res/android/icon/mdpi.png" background="@color/background" density="mdpi" foreground="/res/android/icon/mdpi-foreground.png" />
```
背景をbackground、フロントをforeground、Android 8.0以前の端末向けはsrcにそれぞれ設定。

## config.xmlで設定したリソースがコピーされていない

Cordova 9.0（正確にはcordova-android 7.0以降）からAndroid Studio形式のディレクトリ構成になったため、targetのパスを変更する必要あり

例えば

```xml
<resource-file src="/res/android/notification_icon/mdpi.png" target="res/drawable-mdpi/notification_icon.png"/>
```

は

```xml
<resource-file src="/res/android/notification_icon/mdpi.png" target="/app/src/main/res/drawable-mdpi/notification_icon.png"/>
```

にする（頭に「/app/src/main/」をつける）

## InAppBrowserで"NSURLErrorDomain error -999"が発生する
"NSURLErrorDomain error -999"はリクエストがキャンセルされた際のエラーとのこと。
ほとんどの場合は無視すればよいため、無視する様修正。

以下参考
https://github.com/apache/cordova-plugin-inappbrowser/issues/329
https://github.com/dropbox/dropbox-sdk-js/issues/196

## Android 9で実行するとHTTP（非TLS）通信が失敗する
targetSdkVersion=28（MonacaのCordova 9.0環境はtargetSdkVersion=28になります）でビルドしたアプリをAndroid 9で実行すると、デフォルトでHTTP（非TLS）が失敗します。
今回問題になったケースだと、WebViewで非TLSのWebページを開くと、「ERR_CLEARTEXT_NOT_PERMITTED」というエラー表示になりました。
↓以下参照
https://developers-jp.googleblog.com/2018/05/protecting-users-with-tls-by-default-in.html

以下ページにある通り、回避策はいくつかあります。
https://qiita.com/superman9387/items/7441998138a8509537a4

今回問題になったケースだと、どこで非TLSの通信をしているか特定しきれないため、 `android:usesCleartextTraffic` を `true` にする方法で対応しました。
CordovaでAndroidManifest.xmlのapplicationタグの属性を書き換えるには、config.xmlに以下の記述を行う方法があります。

```xml
<edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
  <application android:usesCleartextTraffic="true" />
</edit-config>
```

が、この設定を追記してCordova 9.0環境でビルドすると、AndroidManifest.xmlのタグが欠落するようで、おかしなことになります。
どうやら、Cordova 9.0のバグのようで、Cordova 8.0などでは発生しませんでした。
↓バグレポート
https://issues.apache.org/jira/browse/CB-13514

仕方ないので、Hookスクリプトを使って、AndroidManifest.xmlを直接書き換える方法で対応しました。

scriptsディレクトリをルート直下に作成して、以下のファイル（merge-app-attr.js）を配置

```js
#!/usr/bin/env node

module.exports = function (context) {
  const fs = require('fs')
  const path = require('path')
  const xml2js = require('xml2js')
  const parser = new xml2js.Parser()

  const manifestPath = path.join(context.opts.projectRoot, 'platforms/android/app/src/main/AndroidManifest.xml')
  const manifestXml = fs.readFileSync(manifestPath, 'utf8')

  parser.parseString(manifestXml, (err, result) => {
    if (err) {
      console.error(err)
      return
    }
    const applicationTag = result['manifest']['application'][0]['$']
    applicationTag['android:usesCleartextTraffic'] = 'true'
    const builder = new xml2js.Builder()
    const xml = builder.buildObject(result)
    fs.writeFileSync(manifestPath, xml, 'utf8')
  })
}
```

config.xmlに以下を追記（platform=androidのタグ配下に、hookタグを追記）

```xml
<platform name="android">
  <hook type="after_prepare" src="scripts/merge-app-attr.js" />
  <!-- ... -->
</platform>
```

※AndroidManifest.xmlを読み込んでパースして、applicationタグに `android:usesCleartextTraffic="true"` を追記してるだけです。

## 位置情報（緯度経度）が取得できなくなる

### 現象
位置情報（緯度経度）はcordova-plugin-geolocationの`navigator.geolocation.getCurrentPosition` で取得しているが、常にエラーになる。※PositionError（code=1、message=Illegal Access）が返ってくる

どうやら位置情報取得の権限エラーの様だが、ユーザーが位置情報取得の権限を許可した状態でも位置情報が取得できない。

### 原因

以下の設定がAndroidManifest.xmlにマージされていないかったため。（以下の設定がないと、GPSから位置情報を取得できない）

```xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

Cordovaプラグインはplugin.xmlというファイルに、AndroidManifest.xmlにマージする内容を設定できる。
cordova-plugin-geolocationは以下の設定が記述されていたが、何らかの原因で正しく処理されていなかった。

```xml
<config-file target="AndroidManifest.xml" parent="/*">
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-feature android:name="android.hardware.location.gps" />
</config-file>
```

### 根本原因

Cordova 9.0のバグ
https://github.com/apache/cordova/issues/95

問題となったアプリでは、config.xmlに以下の記述があった。

```xml
<config-file target="AndroidManifest.xml" parent="/*">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
</config-file>
```

`<config-file target="AndroidManifest.xml" parent="/*">` はAndroidManifest.xmlの直下にタグを挟み込む設定だが、この設定が被るとうまくマージされなくなる模様。

### 対応

```xml
<config-file target="AndroidManifest.xml" parent="/*">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
</config-file>
```

は、minSdkVersionを21に変更する設定のため、以下の設定に書き換えた。

```xml
<preference name="android-minSdkVersion" value="21"/>
```