- 投稿日:2020-01-13T19:17:55+09:00
スマホ用VRに6DoF機能やハンドトラッキング機能を実装してみた
(Unityアセットやコードはこちら)
皆さんこんにちは。
早速ですが、VR(バーチャルリアリティー)は勿論ご存じですよね?
ゴーグルを付けてやる、あれです。今では、ゴーグルやコントローラーの位置を認識して、自由に動き回れる6DoFという機能があります。
また、最近ではOculus Questがハンドトラッキングに対応した、ということも聞きます。しかし、このようなVRは、OculusやViveといった、専用のゴーグルが必要で、そのために手軽に試せない、という印象があると思います。
私も実は持っていません。しかし、スマホをはめるだけで出来る簡易的なゴーグルも存在します。
今では100均でも売っているそうです。
これであれば、安い出費で手軽にVRが体験できる、ということです。
ただ、現状では6DoFやハンドトラッキングに対応したものがほとんどありません。ということで、実際に作ってみました。
といっても、一から作るのは私には無理なので、既存の技術を流用します。
開発環境はUnityで、現在はAndroidのスマホのみに対応しています。まず、AndroidスマホにはARCoreという機能があります。
その名の通り、ARに使われるもので、これを使えばスマホのカメラの内容から、ゴーグルの位置を認識することができるようになります。
これによって、6DoFのように、自由に動き回れるようになります。
これは実は過去にやったことがありますが、新しいバージョンのUnityには対応できていなかったので、AR Foundationを使う形で実装し直しました。次に、ハンドトラッキングを実装するためにMediaPipeというものを使いました。
これは機械学習のモデルを利用するのに使うライブラリだとのことですが、そのサンプルの1つにハンドトラッキングがあるので、それを使いました。
ただ、Unityではそのままでは使えないので、それで実行できるように一部変更しました。
ゴリ押し気味に実装したので、性能が若干落ちたような気もしますが、なんとかなってます。このように自由に動き回ったり、
手を認識させて動かしたりすることが出来るようになりました。
前述の通り、ARCoreに対応したAndroidスマホと、スマホVR用のゴーグルがあれば、高価なゴーグルが無くとも試すことができます。
ちなみに、このハンドトラッキングで、女の子のおっぱいを触れたり、スカートをめくったりすることが出来るアプリを開発しましたが、見事にリジェクトされました。
公開できればネタとして最高だと思っておりましたが、仕方がないですね。また、Githubでアセットやコードを公開しているので、6DoFやハンドトラッキングといったこれらの機能を使いたいという方は、ぜひ使ってみてください。
以上です。
- 投稿日:2020-01-13T18:55:41+09:00
Termux + Flutter + dart ffi で .so リロード対応し Android C/C++ + GUI アプリ開発を効率化するメモ
背景
- C/C++ がメインの Android アプリを開発している(GLES/Vulkan グラフィックス, レイトレーシング, 画像処理, 機械学習など)
- 実機で動かして実際のパフォーマンスを知る必要がある, エミュレーターでは実行が対応していない or 実行が遅すぎる
- PC で NDK でクロスコンパイル -> apk 転送 -> 実行がめんどい
- グラフィックや機械学習系だと lldb デバッグはなかなかフィットしないし, デバッグとしては画像などを出したい
その上で, C/C++ メインアプリに UI や, Android のカメラ画像などの取得が欲しい(カメラ画像に対するリアルタイム StyleTransfer アプリ開発ととか)
- imgui や SDL で GUI だとスマホの画面だと操作しずらい. カメラからの画像とか使いたいときに連携がめんどい
- Kotlin + Jetpack UI でもいいが, iOS, PC とも統一できて, hot reload ができ, 最近(?) native module のロードに対応した Flutter を使いたい.
- Flutter Desktop + native code アプリを作る準備メモ(2020 年 1 月 7 日時点) https://qiita.com/syoyo/items/7e2c17261089e0027c30
- これにより, ある程度 PC での開発とコードを合わせたりができる
Termux で native コンパイル(clang)して, その .so を Flutter(dart ffi)で呼ぶという仕組みを作って, 開発の効率化をはかってみます.
Known issue
.so のリロードで問題がありました. 手間をかければいけますが... 詳細は後述します.
必要なもの
- non-rooted な Android(つまり普通に市販されている Android スマホ)
- Cortex-a72 以降あたりのアーキテクチャで out-of-order 実行でメモリ 4GB or more で性能のよいスマホかタブレット(最近はノート PC よりもスマホのほうが高性能ですよね)
- 今回は Pixel4(Snapdragon 855) を使いました.
- Termux + clang など開発環境を pkg install
- 2020/01/05 時点では clang 7 でした.
- PC 開発環境
- Android 実機(スマホ)とは usb でつなげている
前提
Flutter のアプリは, 実機では arm64 で動いているものとします.
(Flutter デフォルトでは, x86 32bit 64bit, arm 32bit 64bit の四種類全部対応の apk を作っている)
Pixel4(Android 10) ではデフォルト arm64 のようでした.
(Flutter(Dart)上で 32bit か 64bit か確認する API はあるのかな?)https://flutter.dev/docs/development/platform-integration/c-interop
を参考に, Flutter アプリ側で Cmake で native module をビルドしておく仕組みを作っておきます.
(その後, アプリをいったん起動したのち実行時に .so を入れ替えるため)C/C++ コンパイル
Termux 上で .so をつくる
pkg で入る clang を使って,
-fPIC -c -std=c++14 -stdlib=c++
あたりでコンパイルして, .so を作ります.
(clang 7 では C++17 も対応しています)Termux での clang では
ANDROID
などは定義されませんので, 将来的にアプリを APK で publish する場合, Android 依存のものについてはいくらか注意してコードを書く必要があります.
たとえば, printf や pthread(bionic では pthread cacnel など一部実装されていない) やファイルアクセス周りなど(Android では APK assert のファイル書き込みなどは C++ レイヤーではできない. NDK clang では-std=c++17
でも std::filesytem は対応していない)リリース間際になってきたら, PC で ndk-build してコンパイル確認したほうがいいかもしれません.
Android で .so を Flutter app に転送するしくみをつくる
Optional: Flutter アプリの storage 設定
storage にアクセスするために, STORAGE permission が必要かもしれません.
デフォルトでは提供されていないようで, permission_handler などの plugin を使う必要があります.https://pub.dev/packages/permission_handler
このあたりは web にいろいろ資料がありますので, それらを参考にして設定しておきましょう.
Termux で /sdcard にアクセスできるようにする
/sdcard
or/storage/emulated/0
(基本どちらもおなじ) に .so を配置するため,https://android.stackexchange.com/questions/205430/what-is-storage-emulated-0
Termux で storage アクセスのセットアップをしておきます.
https://wiki.termux.com/wiki/Termux-setup-storage
セットアップしますと,
~/storage
が/storage/emulatated/0
への symlink として生成されます.Android での .so のアクセスについて
Using Dynamic linked libraries in android
https://hero.handmade.network/forums/code-discussion/t/750-using_dynamic_linked_libraries_in_androidAndroid 7 からセキュリティが厳しくなり, .so は
/data/data/<app>/
(アプリ固有のストレージ領域)からしか dlopen できません(https://developer.android.com/about/versions/nougat/android-7.0-changes#ndk
Termux でコンパイルした .so を
/sdcard
などに配置して, そこからdlopen("/sdcard/mylib.so");
などとするとエラーになります(ファイルは storage permission を付ければ open はできるが, symbol の取得でエラーになる)./data/local/tmp
もダメでしたadb run-as で .so をアプリストレージに転送
Android: /data/data配下にadb push
http://yuki312.blogspot.com/2019/01/android-datadataadb-push.htmlPC から, 実機に adb ログインして run-as で Flutter app の id になればいけます.
(Flutter app を debug モードでビルドしている必要があるかと思います)
いったん最初に Flutter アプリを Android で動かし,/data/data/<appid>
を作っておく必要があると思います.
<appid>
は AndroidManifest.xml でわかります.(Flutter の場合はandroid/app/src/main/AndroidManifest.xml
にあります).
adb exec-in
で, PC から/data/data
に直接ファイル転送はうまくいきませんでした(Android 10). exec-in は run-as と組み合わせての実行ができませんでした(exec-out はできる)..so 転送の手順
- Termux で .so をビルドする.
- Termux で .so を
/sdcard/
にコピーする(/data/local/tmp
は後述の cp/cat が Pixel4 ではうまくいかなかった)- PC から adb shell して adb run-as する
- adb の shell で
cat /sdcard/mylib.so > /data/data/<appid>/files/
orcp /sdcard/mylib.so /data/data/<appid>/files/
でfiles
ディレクトリ(files
フォルダは必須ではないが, デフォルトで用意されているので使っています)ちょっとめんどいですね. 何かしらスクリプトとか(rsync or file watch スクリプトとか), vscode 拡張とか書けば効率化できるでしょうか?
adb exec-in で run-as で PC から
/data/data/<appid>/
に直接ファイル転送もいけるはず?C++ コードが安定してきたら, 普通の Android C++ 開発のように, PC 側で cross-compile というのも手かもしれません.
libc/libm, libc++
Termux で printf や sin() など, libc/libm 関数を使うと, Flutter(Android bionic)で動かすと ABI が合わないのかクラッシュしますので注意です.
Gradle cmake では, C++ STL はデフォルトでは c++-static です.
Termux でコンパイルした .so は
libc++_shared.so
に依存するので,
gradle 設定でarguments "-DANDROID_STL=c++_shared"
を指定し, c++_shared にして libc++_shared.so が Flutter の apk に含まれるようにしておきます.
(Termux 側で頑張って STL static リンクするのもできるのかな?...(試したかぎりではいくつか .a などが足りなかった))
android/app/build.gradle
android { compileSdkVersion 28 sourceSets { main.java.srcDirs += 'src/main/kotlin' } lintOptions { disable 'InvalidPackage' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.flutter_native_vulkan_experiment" minSdkVersion 16 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { arguments "-DANDROID_STL=c++_shared" } } } ...ちなみに, これにより Apk に含まれる arm64-v8a の
libc++_shared.so
は 300 kb くらいとそんなにファイルサイズは大きくありませんでした.NanoSTL
std::vector は動くようですが, いろいろ C++ コード書いていったら Termux(Linux) と Flutter(Android) で STL の ABI とかが合わないことになるかもしれません.
使っている STL が vector, string, map くらいであったら, NanoSTL https://github.com/lighttransport/nanostl の利用も検討してみましょう. libm の実装も少しあるよ(ただし近似関数での実装. それなりに精度はあるとおもうが correctly rounded で完全(i.e, 0.5 ulp 以下)ではない)
Flutter(Dart) 側のコード
DynamiLibrary.open("/data/data/<appid>/files/mylib.so")
という感じで .so を指定します..so リロードやっぱり簡単にはできない
しかし, Android SELinux がらみで, .so 差し替えて flutter hot reload or C レイヤーで
dlclose
,dlopen
し直すとセグフォしました.F/libc (27288): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x9d0 in tid 27330 (Thread-2), pid 27288 (lkan_experiment) *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'google/flame/flame:10/QQ1B.200105.004/6031802:user/release-keys' Revision: 'MP1.0' ABI: 'arm64'Linux(Android) 側で, アプリが実行中に保持している .so と, ファイルが挿し変わっていたらエラーになるようです.
Flutter/Dart -> [DynamicLibrary.open] -> stub.so -> [dlopen] -> module.soという二段構えも試しましたが(
stub.so
はアプリ実行中に挿し変わらない), やはり stub.so での dlopen でエラーになりました.解決策としては,
- 関数を呼び終わったあとに stub.so から, module.so を毎回 dlclose する
- .so を書き換えるときに, まずは Flutter に通知を出して, dlclose() を読んでから, .so ファイル差し替え
毎回 dlclose は対応自体は楽ですが, グラフィックスで毎フレーム呼び出す関数などでは dlopen/dlclose のコストがかかりそうです.
必要なときだけ dlclose したいですが, 残念なことに, Flutter(Dart) では, 現状 DynamicLibrary.close をサポートしていません.
https://github.com/dart-lang/sdk/issues/40159
したがって, stub.so を用意して多段 .so の構造を使うのは必須になります.
- stub.so
- 各種エントリポイント
- module.so を unload(dlclose) する機能を追加
.so hot reload するには,
- C++ コード編集し, module.so ビルドする
- Flutter へ Unload させるコマンドを出す(Flutter 側で hot reload 検知 https://stackoverflow.com/questions/55281077/how-to-detect-hot-reload-inside-the-code-of-a-flutter-app, or JSON-RPC あたりで通信?)
- Flutter が Unload メソッド呼ぶ
- adb で module.so 転送
- Flutter hot reload して module.so を再度読み込み
という仕組みを作る必要があります.
感想
とりあえず Termux 連携 + .so リロードできる仕組みを作りましたが, adb で .so 転送で PC を介する必要があったり, printf 使えないとか STL 絡みの不都合が将来的にあるかも, というのを考えると, PC で Android cross compile してビルド + .so 転送のほうが開発楽かもという気がしてきました...
参考までに, PC(Linux) で Cmake で NDK toolchain 使って C/C++ アプリをコンパイルする設定サンプルです.
.so リロードも現状手間がかかることがわかりました.
ここまでやっておいてなんですが, PC で C/C++ 開発 + API を先にある程度決めておき, あとはデータを JSON-RPC などでやりとりなどして, なるべく .so リロードの必要性をなくすほうがいいかもしれません....cc の watch
PC 側で作業するとき, Flutter の hot-reload では, native code(.cc ファイル) までは watch してくれません.
Linux 限定であれば entr(1) http://eradman.com/entrproject/ (Ubuntu 18.04 なら apt で入ります), クロスプラットフォーム重視なら node.js + file watch するアプリで, .cc が変更されたら build & adb run-as して .so 転送するしくみを構築するとよいかなと思います.
Flutter に hot reload リクエストを送るのは, VSCode でダミーの .dart を touch するのが楽です.
手動で Flutter Android アプリに hot reload 命令を送るメモ(2020 年 1 月 14 日時点)
https://qiita.com/syoyo/items/37d5f22689bb50217858TODO
- .so を fread or mmap して, elf バイナリを解析して直接エントリポイント叩く方法も考えてみる(
dlopen
しない)- non-rooted 実機だとセキュリティ周りでいろいろ制限あり面倒なので, Vulkan 1.1 対応の開発ボードで ASOP で開発環境整えたい
- 既存のもとして hikey960 が Android 標準(?)の開発ボードとしてあるが, GPU の Vulkan 対応が 1.0 まで. あとは Mali なので GPU 遅い
- Termux で動く adb が使えるかためす https://github.com/MasterDevX/Termux-ADB
- とりあえず試してみたところ, 自分自身(localhost:5555) に adb すると, ポート関連でエラーになった...
- C/C++ のコードが大きいと, termux ではコンパイル遅かったりメモリ足らずなどでコンパイルがこけたりするかもなので(Pixel4 だとメモリ 6GB ありそれなりに快適であった), Jetson AGX などパワフルな Arm 開発ボード で .so をクロスコンパイルし, adb or JSON-RPC あたりでネットワーク転送して .so リロードの仕組みを作る
- termux への ssh ログインは, wifi 接続だと termux が不安定な気がするので, USB イーサネットを使ってみる
- 優秀な Flutter desktop + C++ 若人さまが, 人類史上最速で優秀な Flutter Android + C++ 若人さまへと昇華なされるスキームを確立する旅に出たい.
- 投稿日:2020-01-13T18:17:29+09:00
[Android / Kotlin] ノッチ(ディスプレイカット / 切り欠き)の部分も表示する方法
ノッチを付けずに戦ってるメーカーはかっこいいと思います。
ノッチ、ディスプレイカット、切り欠きとは
iPhone X(嘘ですEssential Phoneです)から始まった上のかけている部分。この部分にはカメラや顔認証に必要な部品があったりする(顔認証対応端末のみ)。
中華系が真似したせいで流行ってしまった?Pixel 3 XLに関しては他端末よりも大きめのノッチがついてたりする。(カメラが自撮り側のみ2つついているため。広角だよ←使ったことないけど)
基本的にはこの部分にはステータスバー(?や?や?等があるバーのこと)があってアプリは侵略できない。
ノッチがない端末でノッチを出す方法
開発者向けオプション→ディスプレイカット→縦長のカットアウト
を選択することでノッチを表示できます。
ステータスバーが広くなって違和感あると思うよ。ノッチがある端末でノッチを隠す(利用しない)方法
Pixel 3 XLでの場合です。
開発者向けオプション→ディスプレイカット→非表示
を選択することでノッチを利用しない設定にできます。その他サードパーティのアプリでノッチ領域の背景を黒くするアプリなどもあるのでどうしても気になる人は入れてみては?。
侵略するには
こんなふうに上にも表示させたい場合は、
//ステータスバー透明化+タイトルバー非表示+ノッチ領域にも侵略 window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) supportActionBar?.hide() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val attrib = window.attributes attrib.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES }これで動きます。
ActionBarを非表示にしてますが、どっかでそもそも表示しない設定にしている場合は
二行目を消したほうがいいと思います。これでノッチ領域に侵略することに成功しました。
以上です。お疲れさまでした。888888888
参考にしました
https://stackoverflow.com/questions/49190381/fullscreen-app-with-displaycutout
android display cutout full screen って検索したんですけどGoogle先生すごい。
- 投稿日:2020-01-13T15:30:30+09:00
Delphiでインテントからファイルを受け取ってみる
はじめに
前回の記事と対になるものです。 Delphi 10.3 CommunityEditionにて確認しています。
暗黙的インテントを受け取る
参考:
Delphiサンプル FMX.Android Intents Sample
Delphi 10 SeattleのAndroidアプリケーションでIntentの送受信をするにはインテントフィルタ
最初に
AndroidManifest.template.xml
を編集し、受け取りたいインテントの種類に応じた内容を追加します。
例えば、Googleドライブでファイルを選択し自アプリで開く場合は、VIEWというアクションになります。(拡張子がTxtなどのドキュメント形式の場合、EDITというアクションになるので注意)<activity android:name="com.embarcadero.firemonkey.FMXNativeActivity" (省略)> (中略) <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/octet-stream" /> </intent-filter> </activity>コード
インテントの受信の処理は、アプリケーション動作前(停止中)と、アプリケーション動作中でインテントの取得方法が違うため、コードが分かれます。先に、アプリケーション動作中について書きます。
アプリケーション動作中のインテント処理
interface節のusesに次を加えます。
System.Messaging, Androidapi.JNI.GraphicsContentViewTextメインフォーム(TForm1)のメソッドに次を追加します。
procedure HandleActivityMessage(const Sender: TObject; const AMessage: TMessage); procedure HandleIntentAction(const intent: JIntent);implementation節のusesに次を加えます。
FMX.Platform.Android, Androidapi.HelpersFormの
OnCreate
イベントに以下を記述します。procedure TForm1.FormCreate(Sender: TObject); begin MainActivity.registerIntentAction(TJIntent.JavaClass.ACTION_VIEW); TMessageManager.DefaultManager.SubscribeToMessage (TMessageReceivedNotification, HandleActivityMessage); end;
MainActivity.registerIntentAction
の引数は、インテントフィルタに設定したものと合わせます。
複数種類のインテントを受ける場合、複数行に分けて書きます。次にHandleActivityMessageを実装します。
procedure TForm1.HandleActivityMessage(const Sender: TObject; const AMessage: TMessage); var intent: JIntent; begin if AMessage is TMessageReceivedNotification then begin intent := TMessageReceivedNotification(AMessage).Value; if intent <> nil then HandleIntentAction(intent); end; end;HandleIntentActionを実装します。(ここでは動作確認のみ。ファイル処理は後半で扱います。)
procedure TForm1.HandleIntentAction(const intent: JIntent); begin ShowMessage(JStringToString(intent.toString)); end;一旦動作確認してみましょう。(GoogleドライブにTest.datというファイルを置いて開いてみました)
AndroidManifest.xml が古い場合や、内容が違う場合、ファイルを選択した際に候補として出てこないことがあります。その場合はプロジェクトのクリーンアップ後に再度実行してみてください。アプリケーション起動時のインテント処理
メインフォームに次のメソッドを追加します。
procedure HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject);FormのOnCreateイベントに以下を追加します。
procedure TForm1.FormCreate(Sender: TObject); var AppEventService: IFMXApplicationEventService; begin if TPlatformServices.Current.SupportsPlatformService (IFMXApplicationEventService, AppEventService) then AppEventService.SetApplicationEventHandler(HandleAppEvent); (上で書いた処理) end;HandleAppEventを実装します。
function TForm1.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean; var StartupIntent: JIntent; begin result := False; //(ここちょっとよく理解できていません) if AAppEvent = TApplicationEvent.FinishedLaunching then //起動時イベントの場合 begin StartupIntent := MainActivity.getIntent; if StartupIntent <> nil then begin HandleIntentAction(StartupIntent); end; end; end;デバッグ実行し、一度アプリを終了してからインテントを受け取れるか確認してみましょう!
インテント内のファイルの抽出
先ほどのHandleIntentAction を書き変えていきますが、まずimplementation節のusesに以下を追加します。
Androidapi.JNI.Net, //Jnet_Url Androidapi.JNI.JavaTypes, //JInputStream Androidapi.JNIBridge, //TJavaArray Androidapi.JNI.Provider; //TJOpenableColumnsファイルの有無
インテントにファイルが含まれているかどうかは、
intent.getData
がnil
かどうかで判定できます。var uri:JNet_Uri; begin uri := intent.getData; if uri <> nil then begin (ファイルがある場合の処理) end; end;(ファイル以外の情報をとる場合、
intent.getExtras(StrintgToJString('キー名'))
を使用します。今回は割愛します。)ファイルデータの取得
URIが取得できれば、ファイルの中身は
JInputStream
にてとることができます。それをTMemoryStream
に転写します。var inputStream: JInputStream; jbytes:TJavaArray<Byte>; memoryStream:TMemoryStream; begin inputStream := TAndroidHelper.ContentResolver.openInputStream(uri); jbytes := TJavaArray<Byte>.Create(inputStream.available); inputStream.read(jbytes); memoryStream := TMemoryStream.Create; memoryStream.Write(jbytes.Data^,jbytes.Length); inputStream.close; end;そこから先は普段のDelphiと変わりません。
ファイル名の取得
URI の内容が file://~ から始まっている場合 ファイル名はURI内に含まれていますが、content:// から始まっている場合は Android OSの仕組みである、ContentResolverを使用する必要があります。
参考:【Android】「Uri」から「ファイル名」を取得するvar cursor:JCursor; filename:String; begin if uri.getScheme.equals(TJContentResolver.JavaClass.SCHEME_FILE) then begin filename := JStringToString( Uri.getLastPathSegment ); end else if uri.getScheme.equals(TJContentResolver.JavaClass.SCHEME_CONTENT) then begin cursor := TAndroidHelper.ContentResolver.query(uri, nil, nil, nil, nil); try if (cursor <> nil) and (cursor.moveToFirst) then begin filename := JStringToString( cursor.getString(cursor.getColumnIndex(TJOpenableColumns.JavaClass.DISPLAY_NAME) )); end; finally cursor.close; end; end; //filenameが空だった時はデフォルト名か何かをつける end;(projectionを指定しなくてもDisplayNameとSizeはとれるからいいのかなーという雑な認識)
アプリケーションローカルにファイルのコピーを保存する場合
上記を通しで書くとこんな感じです
procedure TForm1.HandleIntentAction(const intent: JIntent); var uri:JNet_Uri; cursor:JCursor; filename:String; inputStream: JInputStream; jbytes:TJavaArray<Byte>; memoryStream:TMemoryStream; begin uri := intent.getData; if uri <> nil then begin if uri.getScheme.equals(TJContentResolver.JavaClass.SCHEME_FILE) then begin filename := JStringToString( Uri.getLastPathSegment ); end else if uri.getScheme.equals(TJContentResolver.JavaClass.SCHEME_CONTENT) then begin cursor := TAndroidHelper.ContentResolver.query(uri, nil, nil, nil, nil); try if (cursor <> nil) and (cursor.moveToFirst) then begin filename := JStringToString( cursor.getString(cursor.getColumnIndex(TJOpenableColumns.JavaClass.DISPLAY_NAME) )); end; finally cursor.close; end; end; if filename.isEmpty then fileName := TPath.GetTempFileName; inputStream := TAndroidHelper.ContentResolver.openInputStream(uri); jbytes := TJavaArray<Byte>.Create(inputStream.available); inputStream.read(jbytes); memoryStream := TMemoryStream.Create; memoryStream.Write(jbytes.Data^,jbytes.Length); inputStream.close; memoryStream.SaveToFile(TPath.Combine(TPath.GetDocumentPath, filename)); memoryStream.DisposeOf; end; end;以上です。
- 投稿日:2020-01-13T13:21:03+09:00
【Androidアプリ】AMD CPU RyzenのPCでエミュレータを使用する方法
概要
- 開発環境によっては、Android Virtual Device(AVD)を起動することができません(ex. Windows 10 Home, AMDプロセッサ)
- Andoridアプリを開発する上でAVDを使用できないと、いちいち実機にインストールしたり、.apkを出力してNox Playerなどのエミュレータで動作確認をしなければならず効率が落ちます
- 当方も最初はこの方法を採っていましたが、デバッグがしづらく途中で我慢ができなくなり、自身の環境でもAVDを使う方法はないか調査しました
-> の結果をまとめたのが本記事です
環境
- OS: Microsoft Windows 10 Home
- プロセッサ: AMD64 Family 23 Model 17 Stepping 0 AuthenticAMD ~2000 Mhz
- BIOS: LENOVO 8PCN45WW
- Android Studio 3.4.1
手順
ハイパーバイザープラットホームの有効化
Windowsの機能の有効化
を開き、Windowsハイパーバイザープラットフォーム
にチェックを入れます。BIOSの設定変更
- PCを再起動して、BIOSの設定変更画面に入ります(起動方法はメーカーによって様々なので、
機種名 BIOS
などで検索してみてください)- 仮想化に関する設定を変更します(ex. AMD SVN Technology = Disabled -> Enabled に変更)
ハイパーバイザーの状態を確認
Hyper-V の要件
を確認します。コンソールから
systeminfo
を実行し、下記の状態になっていれば、AMD CPU RyzenのPCでもエミュレータを起動することができます。Hyper-V の要件: ハイパーバイザーが検出されました。Hyper-V に必要な機能は表示されません。AVDを起動
- Android Studioの右上のメニューからAVD Manager(右から2つ目)を起動します
- 仮想デバイスを作成して、
Launch this AVD in the emulator
で起動します
- このような感じで起動します
- 投稿日:2020-01-13T12:49:45+09:00
Android 動画再生アプリ開発(ExoPlayer)
Android Exoplayerの使い方
Exoplayerとは簡単に言うとAndroidで動画再生をするためのAPIをたくさん用意したライブラリです。
通常1から動画プレイヤーを開発しようとすると、動画デコード、音声デコード、ストリーミング、復号化などを全て作らなければなりませんが、Exoplayerでは簡単なAPIですぐに実装できます。
API 16以上のSDKであれば使用できるのでハードウェアにも依存せず使用できます。
[下記公式URL]
ExoPlayer | Android デベロッパー | Android DevelopersExoplayer導入
初めにExoplayerライブラリをプロジェクトに追加します。
google(), jcenter()追加
まず、build.gradleに下記を追加します。
build.gradlerepositories { google() jcenter() }Exoplayerライブラリ追加
次にExoplayerのライブラリを追加するのですが、Exoplayerはいくつかのライブラリを分けていますが、一括で導入したい場合は下記です。
※2020/01/13時点での最新バージョンは2.11.1build.gradleimplementation 'com.google.android.exoplayer:exoplayer:2.11.1'特定のライブラリのみを追加したい場合、Exoplayerは5つのライブラリに分かれているので、これを指定してください。
Exoplayer
- core
- 動画配信用のコアライブラリ
- dash
- Dashコンテンツ用ライブラリ
- hls
- Hlsコンテンツ用ライブラリ
- smoothstreaming
- smmothstreaming用ライブラリ
- ui
- UIカスタム用のライブラリ
例えば、coreのみを指定する場合は下記です。
ライブラリを分けることはapkサイズの縮小に繋がります。build.gradleimplementation 'com.google.android.exoplayer:exoplayer-core:2.11.1'Java 8追加
build.gradlecompileOptions { targetCompatibility JavaVersion.VERSION_1_8 }Exoplayer View追加
layoutファイルに下記を追加します。
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/playerView" android:layout_width="match_parent" android:layout_height="match_parent" app:surface_type="texture_view"> </com.google.android.exoplayer2.ui.PlayerView>次にjavaファイルに下記を追加します。
private PlayerView playerView; private SimpleExoPlayer player; player = ExoPlayerFactory.newSimpleInstance(this); player.addListener(this); playerView = (PlayerView)findViewById(R.id.playerView); DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this,Util.getUserAgent(this,this.getPackageName())); ExtractorMediaSource extractorMediaSource = new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(url)); playerView.hideController(); playerView.setControllerAutoShow(false); playerView.setPlayer(player); player.prepare(extractorMediaSource); player.setPlayWhenReady(true);[url]に再生した動画のurlを入れてください。
おわりに
Exoplayerはカスタムできる幅も実は多く、今回挙げた簡単な動画再生以外にもたくさんありますので、リアルタイムDASHやAES復号、おしゃれなUIなど作りたい場合はコメントなどやTwitterのDMなどでお知らせください。
以上です。
- 投稿日:2020-01-13T05:02:40+09:00
Androidでも Markdown + PlantUML したい
Androidでも Markdown + PlantUML したい
注記:残念ながら日本語入力ができなくなっていました。現在調査中のため、以下の手順は実施しないでください。
今の所、私の探した範囲では Markdown + PlantUML が書けるAndroidアプリは存在しないっぽい(個別には存在する)ので、Linux(Debian)上で Visual Studio Code と Graphviz をインストールして書けるようにしてみました。
Linux(Debian)を構築していない人は、事前にここを見てDebianの環境を設定してください。Graphviz のインストール
Graphviz は文字列を基に図を書くためのツールです。
その図の中に PlantUML も入っていると思ってください。
なお、Graphviz は Java の上で動くので Java のインストールも必要です。
一緒にインストールします。
- デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > System Tools > File Manager PCManFM
- /data/data/tech.ula/files/1/etc/apt/ までディレクトリを移動する
- sources.list を右クリックして(あるいはタッチパネルで二本指でタップ)コンテキストメニューから Mousepad を選択する
- 以下を適当な位置(私な一番下の位置にした)に入力して保存する
deb http://ftp.de.debian.org/debian buster main deb http://ftp.jaist.ac.jp/debian buster main
- デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > Debian > Applications > Shells > Bash
- 次に以下のコマンドを Bash に入力して実行する。
sudo apt update sudo apt upgrade sudo apt install openjdk-11-jdk sudo apt install graphvizデスクトップ環境を日本語化
Bash で以下のコマンドを実行しデスクトップ環境を日本語化します。
sudo apt install task-japanese sudo apt install task-japanese-desktop sudo dpkg-reconfigure tzdata sudo dpkg-reconfigure locales設定後は再起動してください。
Visual Studio Code
arm64 な CPU 向けには公式で Visual Studio Code を配布していないので、個人のご好意により配布して頂いてる以下のサイトからダウンロードします。
残念ながらLinux(Debian)の Firefox は動かないので、Linux(Debian)上ではなくPCやAndroid等で別途ダウンロードしLinux(Debian)に渡す必要があります。https://github.com/headmelted/codebuilds/releases
私は以下のファイルをダウンロードしました。
code-oss_1.42.0-1575959662_arm64.deb
特に問題がない限り、最新版をダウンロードするのが良いと思います。
ダウンロードしたファイルを、Android の以下の場所に設置します。
私は X-plore で設置しました。/sdcard/Android/data/tech.ula/files/storage
Debian からは以下に見えます。
/storage/internal
Bash で以下のコマンドを実行して Visual Studio Code をインストールします。
ファイル名はダウロードしたファイルに合わせてください。sudo apt install /storage/internal/code-oss_1.42.0-1575959662_arm64.debもし、インストールに失敗する場合には以下のコマンドを実行してください。
依存関係を解決してくれます。sudo apt --fix-broken installVisual Studio Code の設定
そのままだと PlantUML は使えないので拡張機能を設定します。
まずは Visual Studio Code を起動します。
デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > プログラミング > Code - OSS(headmelted)
次に、メニューから View > Extentions で拡張機能を開きます。(Ctrl + Shift + X や左側の柱にある□が4つ並んでいるアイコンでも開けます)
拡張機能の検索枠に以下を入力してインストールを行ってください。
Japanese Language Pack for Visual Studio Code Markdown All in One Markdown Preview Enhanced markdownlint PlantUML拡張機能の説明はしませんので、拡張機能については別途調査をしてください。
全てインストールが終わったら Viual Studio Code を再起動します。起動したらメニューの ファイル > キーボード ショートカット を選択します。
検索枠に以下を入力します。
Markdown Preview Enhanced OpenPreview
キーバインドが Ctrl + Shift + V になっている方を Ctrl + ; (ここは好みで)に設定します。
拡張機能は異なる機能に対して同じキーバインドを設定できますが、有効になるのは一つの拡張機能だけです。
Markdown Preview Enhanced は初期状態では他の拡張機能とキーバインドがかぶっているため、変更する必要があります。
動作確認
Visual Studio Code メニューの ファイル > 名前をつけて保存 でファイル名の拡張子を md にして保存します。
Visual Studio Code では、拡張子が md のファイルを Markdown のファイルとして扱います。
次に、コードブロックに対して、以下のように plantuml と指定することでコードブロック内を PlantUML として扱います。(空行)
```plantuml
@startuml
Class01 <|-- Class02
Class03 *-- Class04
Class05 o-- Class06
Class07 .. Class08
Class09 -- Class10
@enduml
```
(空行)試しに上記の例を記載してみましょう。
記載したら、先程設定したキーバインドの Ctrl + ; を押してプレビュー画面を開きます。
うまくいけば、以下の図(上記の例ではクラス)を表示します。PlantUML の書き方は以下のサイトで確認してください。
参考サイト
64-bit ARMv8 マシン用 graphviz_2.40.1-6_arm64.deb のダウンロードページ
- 投稿日:2020-01-13T05:02:40+09:00
Androidでも Markdown + PlantUML したい(暫定対応)
Androidでも Markdown + PlantUML したい
注記:残念ながら日本語入力ができなくなっていました。現在調査中のため、以下の手順は実施しないでください。
→ 暫定対応で「デスクトップ環境を日本語化」を行わないことにしました。とりあえず使えますが、英語の環境になります。今の所、私の探した範囲では Markdown + PlantUML が書けるAndroidアプリは存在しないっぽい(個別には存在する)ので、Linux(Debian)上で Visual Studio Code と Graphviz をインストールして書けるようにしてみました。
Linux(Debian)を構築していない人は、事前にここを見てDebianの環境を設定してください。Graphviz のインストール
Graphviz は文字列を基に図を書くためのツールです。
その図の中に PlantUML も入っていると思ってください。
なお、Graphviz は Java の上で動くので Java のインストールも必要です。
一緒にインストールします。
- デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > System Tools > File Manager PCManFM
- /data/data/tech.ula/files/1/etc/apt/ までディレクトリを移動する
- sources.list を右クリックして(あるいはタッチパネルで二本指でタップ)コンテキストメニューから Mousepad を選択する
- 以下を適当な位置(私な一番下の位置にした)に入力して保存する
deb http://ftp.de.debian.org/debian buster main
- デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > Debian > Applications > Shells > Bash
- 次に以下のコマンドを Bash に入力して実行する。
sudo apt update sudo apt upgrade sudo apt install openjdk-11-jdk sudo apt install graphvizVisual Studio Code
arm64 な CPU 向けには公式で Visual Studio Code を配布していないので、個人のご好意により配布して頂いてる以下のサイトからダウンロードします。
残念ながらLinux(Debian)の Firefox は動かないので、Linux(Debian)上ではなくPCやAndroid等で別途ダウンロードしLinux(Debian)に渡す必要があります。https://github.com/headmelted/codebuilds/releases
私は以下のファイルをダウンロードしました。
code-oss_1.42.0-1575959662_arm64.deb
特に問題がない限り、最新版をダウンロードするのが良いと思います。
ダウンロードしたファイルを、Android の以下の場所に設置します。
私は X-plore で設置しました。/sdcard/Android/data/tech.ula/files/storage
Debian からは以下に見えます。
/storage/internal
Bash で以下のコマンドを実行して Visual Studio Code をインストールします。
ファイル名はダウロードしたファイルに合わせてください。sudo apt install /storage/internal/code-oss_1.42.0-1575959662_arm64.debもし、インストールに失敗する場合には以下のコマンドを実行してください。
依存関係を解決してくれます。sudo apt --fix-broken installVisual Studio Code の設定
そのままだと PlantUML は使えないので拡張機能を設定します。
まずは Visual Studio Code を起動します。
デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > プログラミング > Code - OSS(headmelted)
次に、メニューから View > Extentions で拡張機能を開きます。(Ctrl + Shift + X や左側の柱にある□が4つ並んでいるアイコンでも開けます)
拡張機能の検索枠に以下を入力してインストールを行ってください。
Japanese Language Pack for Visual Studio Code Markdown All in One Markdown Preview Enhanced markdownlint PlantUML拡張機能の説明はしませんので、拡張機能については別途調査をしてください。
全てインストールが終わったら Viual Studio Code を再起動します。起動したらメニューの ファイル > キーボード ショートカット を選択します。
検索枠に以下を入力します。
Markdown Preview Enhanced OpenPreview
キーバインドが Ctrl + Shift + V になっている方を Ctrl + ; (ここは好みで)に設定します。
拡張機能は異なる機能に対して同じキーバインドを設定できますが、有効になるのは一つの拡張機能だけです。
Markdown Preview Enhanced は初期状態では他の拡張機能とキーバインドがかぶっているため、変更する必要があります。
動作確認
Visual Studio Code メニューの ファイル > 名前をつけて保存 でファイル名の拡張子を md にして保存します。
Visual Studio Code では、拡張子が md のファイルを Markdown のファイルとして扱います。
次に、コードブロックに対して、以下のように plantuml と指定することでコードブロック内を PlantUML として扱います。(空行)
```plantuml
@startuml
Class01 <|-- Class02
Class03 *-- Class04
Class05 o-- Class06
Class07 .. Class08
Class09 -- Class10
@enduml
```
(空行)試しに上記の例を記載してみましょう。
記載したら、先程設定したキーバインドの Ctrl + ; を押してプレビュー画面を開きます。
うまくいけば、以下の図(上記の例ではクラス)を表示します。PlantUML の書き方は以下のサイトで確認してください。
参考サイト
64-bit ARMv8 マシン用 graphviz_2.40.1-6_arm64.deb のダウンロードページ
Chromebook C101PAでVSCodeを使う。(Crostini使用)
(やらないで!)デスクトップ環境を日本語化
残念ながら、task-japanese-desktop をインストールすると Anthy が使えなくなるところまで分かりました。
そこから日本語入力を使えるようにするところまで調査を終えておりません。
Bash で以下のコマンドを実行しデスクトップ環境を日本語化します。
sudo apt install task-japanese sudo apt install task-japanese-desktop sudo dpkg-reconfigure tzdata sudo dpkg-reconfigure locales設定後は再起動してください。
- 投稿日:2020-01-13T00:39:59+09:00