dnjiro’s 9VAe blog

誰でもアニメが作れる無料ソフト9VAeきゅうべえ開発者のブログ

DXライブラリをつかって、アンドロイド版 9VAeきゅうべえをつくる

かねてから、アニメーションソフト 9VAeきゅうべえのアンドロイド版の作り方を検討していた。最初は、Xamarin でできるかと思ったが難しいことが判明。その後、DXライブラリを発見。いろんな問題があって解決に半年かかったのでヒントとなった情報をまとめる。iPhone/iPad版9VAeの開発記事はこちら

 

 

完成した9VAeダウンロード 

f:id:dnjiro:20220131141301g:plain



Xamarin が使えなかった理由

Xamrin C#Androidアプリが開発できる環境。9VAeきゅうべえは、本体がC言語で記述されており、Xamrin からCを呼び出せる。しかし、9VAeきゅうべえはエディタで、あちこちで描画関数を呼び出さないといけない。Xamrinは、Win32に対応した関数をもっていて、それはよいのだが、C言語から描画関数(C#)が呼び出せない。

 

DXライブラリとは

つぎに、DXライブラリを見つけた。これは、Cから呼び出せる描画ライブラリ。アンドロイド版があり、Visual Studio でアンドロイドアプリを作成できる。ゲーム用につくられていて、ハードウェア描画をサポートしており高速。すばらしい。ところが、次のような問題があった。

 

  • メニューシステムがない。メニューバーを自分で作らないといけない。メニューバーの下に、9VAeきゅうべえの描画エリアがくるので、メニューバーのメニューを9VAeきゅうべえが表示することにした。今まではOSのメニューを利用していた。
  • ポリゴン塗りつぶしができない。これは痛い。DXライブラリから呼び出す openGL ES は、3次元の三角形の塗りつぶししかできないようだ。9VAeのポリゴンは形が自由自在なので、ねじれたり、穴があいたり、ありとあらゆる形ができないといけない。それができない。ということで、その部分は自力描画する。せっかくのハードウェア描画が使えないがやむを得ない。
  • 文字入力ダイアログがない。これもつらいと思ったら、JNI という仕組みを使って、Javaプログラムを呼び出せばとよいと、DXライブラリのドキュメントにあった。下の記事をみてやってみると、簡単に文字入力できた。これを修正して使う。

    DXライブラリ Android版を使用した Androidアプリで Java のコードを実行する

  • ポリゴン塗りつぶしを自力描画すると、DXライブラリと自力描画が混在して複雑極まりない。

 

結論として、DXライブラリはよくできている。いろんな問題点は回避できる方法があるようだ。というわけで、9VAeきゅうべえアンドロイド版は、DXライブラリを使うことにした。

 

Android Studio インストール

f:id:dnjiro:20220130160604g:plain



  1. ダウンロードしたAndroidStudio を起動。「Next」
  2. 「Standard」にチェック。「Next」
  3. 背景の色を選ぶ。「Next」2回
  4. ライセンスを3項目選び、それぞれ「Accept」。「Finish」

 

これでインストールが行われます。まず、DxLibのサンプルを読み込んでみましょう。

  1. AndroidStudioの起動画面で、「More Actions」をクリック(上図14)。メニューから「Import Project(Gradle Eclipse ADT etc)」
  2. ダウンロードしたDxLibを展開、中にある「RunSampleFolder_AndroidStudio」を選ぶ。
  3. プロジェクトを信用するか聞かれるので「TrustProject(信用する)」(上図15)をクリックすると読み込みが始まります。

アンドロイド実機をUSBでつなぐ

DxLibサンプルプロジェクトを実行 (Android Studio)

f:id:dnjiro:20220130181940g:plain

  1. 実機をUSBケーブルで接続。正しく接続されると(上図1)No Devices の表示が実機の機種名に変わります(上図2)実機のリセット、ケーブルの抜き差しを試してみてください。
  2. DxLibのサンプルプログラムを表示するには、左側「app」「cpp」「native-lib.cpp」をクリックすると、右側にソースコードが表示されます。ここにDXLibの関数を書いて動作確認するとよいでしょう。
  3. アプリを作成してみます。「Build」メニュー>「Rebuild Project」でアプリを作り直します(上図6)
  4. 下のBuild タブ(上図7)をクリックすると作成結果が表示されます。この例ではエラー(Javaのバージョンが違っている)と表示されました。
  5. Javaのバージョンは、「File」メニュー>「Project Structure」で設定します。<AndroidStudioのバージョンによって動作が異なります>.gradle .idle .cxx .tmp フォルダを削除して、もう一度、プロジェクト読み込みからやりなおすと、ヒントが得られるかもしれません。「Unsupported class file major ver.61」エラーが出た場合、Javaのバージョン変更する必要があるみたいです。
  6. 左側「SDK Location」>「Gradle Setting」
  7. 「Gradle JDK」を「11 Version」に変更します。「OK」をクリック
  8. 「Build」メニュー>「Make Project」を実行。今度は「BUILD SUCCESSFUL(成功)」となりました。
  9. 右上のデバッグボタン(上図15)を実行
  10. 実機の上に四角形が描画されたら成功です。
  11. 下にはデバッグ用のタブが表示されます。ソースコードブレークポイントを設定し、そこからステップ実行させたり、変数の中をみたりできます。

AndroidStudio の Tips

  • アプリのバージョンは、左側「Gradle Scripts」の上から2つめの「buil.gradle」 に書く
  • 新しいCプログラムファイルを追加するとき、左側「app」 「cpp」「CmakeLists.txt」に書く
  • ストアに登録する .aabファイルは、「build」メニュー「Generate Signed Bundle / APK...」で作成する

Visual Studio インストール

ビルドエラーが出たとき(VisualStudio)

  • 下のウィンドウで「出力」タブをひらいてエラーコードを確認する

機能の追加 (Visual Studio)

  • Visual Studio で「Unable to resolve project target 'android-23'」と表示された場合、アンドロイドのバージョンXX用ライブラリがはいっていない。
  • Visual Studio > ツール >「ツールと機能を取得」
  • 「個別のコンポーネント」から、android-23 の取得を試みる
  • android-23がなかった場合、プロジェクトのターゲットレベルをインストールされているレベルに変更する。

 

 

 Java アラートの参考文献

 

JNIについて

 

DXライブラリを使った XOR 描画

  • ラバーバンドを実現するために、XOR演算の描画を使いたかったが、それらしい描画がなかったので、DXライブラリサイトで質問するとすぐ答えが返ってきた。

    DXライブラリ質問掲示板

  • DX_BLENDMODE_INVDESTCOLOR 演算を、白(255,255,255)で使うと2回描画すると元に戻るらしい。なるほど。具体的なコードは以下 。

int white=GetColor(255,255,255);

SetBlendMode( DX_BLENDMODE_INVDESTCOLOR ,255); //反転描画

DrawBox( x1,y1,x2,y2, white, TRUE); //TRUEだと中を塗る FALSEだと枠線

SetBlendMode( DX_BLENDMODE_NOBLEND ,0); //描画を元にもどす

 

ファイル処理

 

ネットからSVGをダウンロードする方法

UIViewのスレッドでダウンロードするとエラーが発生するみたいだ。別スレッドにすると落ちなくなった。

URLからファイル名を取り出す方法。

 

その他、いろいろ

縦横回転はどうするの?

Javaがheapエラーするときは、環境変数_JAVA_OPTIONSを設定

 

アプリ画面のキャプチャー方法

VisualStudioから adbコマンドプロンプトを起動し、コマンドで取得するのが簡単。コマンドプロンプトから adb コマンドを使いたい場合はこちら

SDK Platform Tools release notes  |  Android Developers

Windowsでadbコマンドを使う方法! Android SDKを入れてパソコンのコマンドプロンプトから操作しよう

  1. Visual Studio > ツール > AndroidAndroid Adb コマンドプロンプト または 上の方法でコマンドプロンプトから adb コマンドを使う
  2. adb コマンドプロンプトに以下を入力すれば、パソコンのコマンドプロンプトを起動したフォルダにデータを取得できる。pull で取得。3つめはデータ削除
    adb shell screencap -p /sdcard/screen.png
    adb pull /sdcard/screen.png
    adb shell rm /sdcard/screen.png

画面操作を動画で記録

  • screenrecord で動画キャプチャできる
    adb shell screenrecord /sdcard/screen.mp4
    (Ctrl+C で停止)
    adb pull /sdcard/screen.mp4

 

 

 

アプリ(9VAe)で保存したデータの取得

  1. adb コマンドプロンプトに以下を入力
    adb pull /sdcard/9VAe/xxx.xxx
    

 

 WebViewのリンクボタンで落ちる問題の修正(Android8以降)

Android8 から、WebViewで表示した asset のHTMLドキュメントで外部リンクは落ちないのに、asset内部の分岐が落ちるようになった。Adroid6以前では、WebViewの親が自動的に設定されていたのが、どうも、Android8 のWebViewから明示的に設定しないといけないようになったみたいだ。WebViewを開く java のコードに以下の setWebViewClient を追加すると落ちなくなった。

webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});
webView.loadUrl("file:///android_asset/help/jp/index.html");

file:///android_asset/は、assetを示すURL。そこに help jp というフォルダをおいて、index.html ファイルを入れている。

 

 パーミッション制御

Android6からプログラム実行中にユーザーが権限を追加できるようになった。その対応

ファイル書き込み権限を与えるために以下の対応を入れた。

  1. プロジェクトのパッケージのプロパティ>ターゲット APIandroid-23 にする
  2. 以下の java コードで権限取得(Android6以上で権限をリクエスト)
    private static final int REQUEST_WRITE_STORAGE = 112;

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    requestPermissions(
    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
    REQUEST_WRITE_STORAGE);
    }

 

 多国語対応

1. DXライブラリの、以下の関数を使って、strings.xml の文字列を取得できる

// res/values/strings.xml の string リソースを取得する
// ValueName:string 情報名
// StringBuffer:ValueName が示す文字列を格納するバッファの先頭アドレス
// StringBufferBytes:StringBuffer のサイズ( 単位:バイト )
// 戻り値  -1:指定の string 情報は無かった  -1以外:StringBuffer に必要なバッファのサイズ
extern int GetAndroidResource_Strings_String( const char *ValueName, char *StringBuffer, int StringBufferBytes ) ;

例えば strings.xml

    <string name="test_str">あいうえお</string>

と記述してあった場合

char StringBuffer[ 256 ] ;
GetAndroidResource_Strings_String( "test_str", StringBuffer, sizeof( StringBuffer ) ) ;

を実行することで StringBuffer に『あいうえお』が代入される

 

2. 英語用文字列と日本語用文字列を、言語別のフォルダ res/values/strings.xml と  res/values-ja/strings.xml に入れておけば、設定言語に従って違う文字列が読み出されるので、翻訳ができる。

 

対応バージョンの書き方

 

Android Go

  • Appストアでテストしてもらうと、Android Go (8.1)で起動できない。別のAPKを用意せよ。といわれた。RAMが1GB以下の低価格バージョンらしい。

1GBメモリでもサクサク動く軽量版Android OS「Android Go」が登場 - GIGAZINE

  • 「端末の除外」という仕組みで対象機種から除外できるようだ。

 

Appストアへの登録

  • apkに署名するために、keytool を使うが、これは java の中にはいっている

    Windows10でJavaのkeytoolを使う - Logicky BLOG

  • keytool をつかって、keystore を作成し、それを、Visual Studio に登録するみたいだ。keystore には、住所、名前などをいれる。App ストアに登録する名前と合わせておく。ただ、Visual Studio への keytool の登録がわからなかった。Xamalin にするとできるようになるのかも。直接コマンドで署名する方法で行った。
  • 最初、jarsigner を使って署名する方法ではうまくいかなかった。以下の、apksigner をつかうと署名できた。apksigner は、\android-sdk にはいっている。

    アプリの署名  |  Android Developers

 

 

  • コマンドプロンプトで署名した例。コマンドのフルパスは環境によって違います。
    1.キーストアxxxxx.jks の作成 
    "C:\Program Files\Java\jdk1.8.0_192\bin\keytool" -genkey -v -keystore xxxxx.jks -keyalg RSA -keysize 2048 -validity 10000 -alias xxxxx
    パスワード、名前、所属名、組織名、住所、国名(JP)、はい

    2.キーストアxxxxx.jks で、yyy-unsigned.apk に署名をつけて yyy.apk を作成する
    "C:\Program Files (x86)\Android\android-sdk\build-tools\25.0.3\apksigner" sign --ks xxxxx.jks --out yyy.apk yyy-unsigned.apk

    3.署名できているか確認
    "C:\Program Files\Java\jdk1.8.0_192\bin\keytool" -list -printcert -jarfile yyy.apk
  • 作成した署名つき apk でインストールするには、adb コマンドプロンプトから
    adb install yyy.apk

Google Playにアプリ署名鍵をあずける

 

64bit対応

  • 2019年8月から64bit 版がないとアップデートできなくなる

Ensure that your app supports 64-bit devices  |  Android Developers

  • 32bit版APKと64bit版APKを同時にリリースしてもよい。そのとき、android:versionCode は、異なる番号をつける。64bit版のほうを大きな数字にする。また、APIレベルを必ず異なるようにする。例えば 32bit版は、21-28、64bit版は、26-28のように違うものにする。

 

アイコンデザイン

 

Android10(API29)対応

  • 2020年8月から、Android10(API29)に対応しないとアプリストアにアップロードできなくなった。
  • それには、Androidマニフェストで、targetSdkVersionを29にするとよい(32bitも64bitも同じ)それで9VAeも以下のように設定していたが・・・

<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/> 

  • ところが、targetSdkVersion="29"にすると、セキュリティが厳しくなってファイルの処理がかわり、Android 10 では、DXライブラリで取得したフォルダに読み書きできなくなっていた。げげえ!Android 9 まで保存できていたのに。
  • とりあえずこれを回避するためには、<application の中に以下の宣言を追加するとよいらしい。これで互換性モードになり、Android10, 11 でも保存ができた。

 <application android:requestLegacyExternalStorage="true" 

  •  さて、これをマニフェストに入れるには、ビルドのターゲットAPIを、android-29 にしないと、requestLegacyExternalStorage が未定義エラーになった。
  • ところが、Visual Studio 2017 には、android-29 SDKがなかった。
  • Visual Studio 2019 にも、android-29 SDKがなかった。
  • やむを得ず、Android Studio で、android-29 を取得し、必要なファイルを自力でコピーした。
  • android-29 にすると、エラーが発生したので、プロジェクトのプロパティから、C/C++コマンドライン をみて、-I オプション(インクルードファイルのパス)をチェック。そのフォルダがなければ、Android Studio から29バージョンをコピーしたら、どうにかビルドできた。
  • 今、DXライブラリは、Android Studio でアプリを作成できるようになっている。ところが、9VAeは Visual Studio 2017 で作成したため、同じ署名が Android Studio でもつけられるのかどうかが不明。

 

実機デバッグができなくなったとき

  • adbバージョンがAndroid実機とあわなければ、配置ができなくなる。Android 5/6/8 は Ver.1.0.39 でないと配置できない。
  • 新しく環境構築すると、adb が Ver.1.0.40 になるので、古いAndroid実機でデバッグできなくなるみたいだ。
  • プロジェクトから、ターゲットデバイスや起動するAPKファイルの設定がなくなる、間違えている場合がある。プロジェクトプロパティ>デバッグ から正しく設定する
  • VisualStudio2017の場合、ターゲットAPIにかかわりなく、android-sdk19 がないとデバッグできなくなるようだ

 

Android Studio への移行

 

Chromebook対応

 

Package作成でエラーがでたときの対処

android - How to fix issue Caused by: java.nio.file.NoSuchFileException: app/build/intermediates/external_libs_dex/release/out while signing apk? - Stack Overflow

プロジェクトフォルダで rm -rf .gradle を実行して、.gradleフォルダを削除するとエラーが出なくなった。ルートにある「.gradle」「.idle」フォルダを削除するとプロジェクトが再構築される。

 

 

M1 Macの AndroidStudio で error=86, Bad CPU type がでた場合

softwareupdate --install-rosetta

 

Wacom ペンタブレットへの対応

 

API30対応

 

MP4動画作成

Mobile-FFmpeg

dependencies {
    implementation 'com.arthenica:ffmpeg-kit-full:4.5.LTS'
}

 

Jcodec、MediaCodecとの比較

サンプル(VideoEditor)の調査

FFmpegで動画をつくるときのコマンド

-r 30 -i "読み出し可能なフォルダ/%04d.jpg" -b 6000k -vcodec mpeg4 -pix_fmt yuv420p "書き込み可能なフォルダ/out.mp4"

M4a

 

HTTPからのダウンロード許可

<application 
...
android:usescleartexttraffic="true"
> </application>

Windows11 Android

  • Android™️ 用 Windows サブシステム
  • Windows11で使うには、Amazon appstore をインストールする。メモリ8GB必要
  • API31対応、64bitアプリがWindows 11のストアアプリから、AmazonAppストア経由で実行できる。Amazonストアに登録するだけで、Windows11で公開するかどうか設定するタグが表示される。複数のAPKを登録したとき、1つしか有効にできない点に注意。古いバージョンを有効にすると新しいバージョンを有効にできない。
  • Windowsとのデータのやりとりは、ACTION_VIEWACTION_EDITACTION_SEND、および ACTION_SEND_MULTIPLE がある。
  • Windowsの電卓を起動するサンプルプログラム
Intent intent = new Intent("com.microsoft.windows.LAUNCH_URI");
intent.putExtra("com.microsoft.windows.EXTRA_URI", "ms-calculator:");
 
try {
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Not running in Windows Subsystem for Android™️ (or running on an older build that did not contain this feature).
}

API 31 対応

<application 
...
<activity
android:exported="true"
> </application>

しゃべらせる

 

ファイルピッカー:SAF(StorageAccessFramework)

 

USIスタイラスペン

アカウント確認

2023年から、Googleによる開発者のアカウント確認が必要になりました。Google Play Console から以下の項目の提出が必要です

  • 氏名と住所
  • D-U-N-S ナンバー(組織のみ)
  • Google Play のユーザーが連絡する際に使用するデベロッパーの連絡先電話番号(組織のみ)
  • Google Play のユーザーが連絡する際に使用するデベロッパーのメールアドレス
  • Google が連絡する際に使用する連絡先電話番号とメールアドレス
  • デベロッパー本人であることを確認するための正式な書類
  • 組織を確認するための正式な書類(組織のみ)

個人用アカウント、日本、で確認期限を予約しました。

 

デバッガーの動作がおかしくなった

  • AndroidStudioのデバッガー動作がおかしくなったとき、File > Project Structure からSuggestions を実行するとなおりました。
  • ただし、Suggestionsのなかの、WebKit, ffmpeg のアップデートを実施すると、ビルドできなくなったため、適用していません

Android14 セキュリティ問題

  • Android14 になると、自分で保存したファイル、ファイルアプリで開いたファイルしか、DXLibフォルダで見えなくなった。
  • そのため、ファイルアプリで外部から転送した9VAeアニメを直接みることができない。9VAe開くで、「ファイルから」で読み込んだ単体SVGは上書きもできるが、画像や音の外部ファイルが見えない。
  • そのため、読み込んだSVGを、9VAe内部のフォルダに保存し、画像や音を正しいものに差し替えると正常に戻るが、アプリをインストールしなおすと、自分以外が作成したファイルとみなされ、見えなくなってしまう。
  • 外部ファイルの転送をJavaで行うと改善されるかも。今はCのパスで読み出しており、それがセキュリティ強化でみえなくなっているようす

フォルダのアクセス権限取得

public void openDirectory(Uri uriToLoad) {
   
// Choose a directory using the system's file picker.
   
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

   
// Optionally, specify a URI for the directory that should be opened in
   
// the system file picker when it loads.
    intent
.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uriToLoad);

    startActivityForResult
(intent, your-request-code);
}
  • 取得した権限を次の起動時にリセットしないようにするためのコード
final int takeFlags = resultData.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);