20200113のAndroidに関する記事は10件です。

スマホ用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ではそのままでは使えないので、それで実行できるように一部変更しました。
ゴリ押し気味に実装したので、性能が若干落ちたような気もしますが、なんとかなってます。

この2つを使うことで、
Qiita_1.gif

このように自由に動き回ったり、

Qiita_2.gif

手を認識させて動かしたりすることが出来るようになりました。

前述の通り、ARCoreに対応したAndroidスマホと、スマホVR用のゴーグルがあれば、高価なゴーグルが無くとも試すことができます。

ちなみに、このハンドトラッキングで、女の子のおっぱいを触れたり、スカートをめくったりすることが出来るアプリを開発しましたが、見事にリジェクトされました。
公開できればネタとして最高だと思っておりましたが、仕方がないですね。

また、Githubでアセットやコードを公開しているので、6DoFやハンドトラッキングといったこれらの機能を使いたいという方は、ぜひ使ってみてください。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 を使いたい.

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_android

Android 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 もダメでした :cry:

adb run-as で .so をアプリストレージに転送

Android: /data/data配下にadb push
http://yuki312.blogspot.com/2019/01/android-datadataadb-push.html

PC から, 実機に 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/ or cp /sdcard/mylib.so /data/data/<appid>/files/files ディレクトリ(files フォルダは必須ではないが, デフォルトで用意されているので使っています)

ちょっとめんどいですね. 何かしらスクリプトとか(rsync or file watch スクリプトとか), vscode 拡張とか書けば効率化できるでしょうか?

adb exec-in で run-as で PC から /data/data/<appid>/ に直接ファイル転送もいけるはず?

https://android.stackexchange.com/questions/213454/how-to-passthrough-a-file-from-pc-to-android-and-back-using-exec-in-and-exe

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 するには,

という仕組みを作る必要があります.

感想

とりあえず Termux 連携 + .so リロードできる仕組みを作りましたが, adb で .so 転送で PC を介する必要があったり, printf 使えないとか STL 絡みの不都合が将来的にあるかも, というのを考えると, PC で Android cross compile してビルド + .so 転送のほうが開発楽かもという気がしてきました...

参考までに, PC(Linux) で Cmake で NDK toolchain 使って C/C++ アプリをコンパイルする設定サンプルです.

https://github.com/lighttransport/embree-aarch64/blob/master/scripts/bootstrap-android-cmake-linux.sh

.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/37d5f22689bb50217858

TODO

  • .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++ 若人さまへと昇華なされるスキームを確立する旅に出たい.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android / Kotlin] ノッチ(ディスプレイカット / 切り欠き)の部分も表示する方法

ノッチを付けずに戦ってるメーカーはかっこいいと思います。

ノッチ、ディスプレイカット、切り欠きとは

iPhone X(嘘ですEssential Phoneです)から始まった上のかけている部分。この部分にはカメラや顔認証に必要な部品があったりする(顔認証対応端末のみ)。
中華系が真似したせいで流行ってしまった?

Pixel 3 XLに関しては他端末よりも大きめのノッチがついてたりする。(カメラが自撮り側のみ2つついているため。広角だよ←使ったことないけど)

基本的にはこの部分にはステータスバー(?や?や?等があるバーのこと)があってアプリは侵略できない。

ノッチがない端末でノッチを出す方法

開発者向けオプション→ディスプレイカット→縦長のカットアウト
を選択することでノッチを表示できます。
ステータスバーが広くなって違和感あると思うよ。

ノッチがある端末でノッチを隠す(利用しない)方法

Pixel 3 XLでの場合です。
開発者向けオプション→ディスプレイカット→非表示 
を選択することでノッチを利用しない設定にできます。

その他サードパーティのアプリでノッチ領域の背景を黒くするアプリなどもあるのでどうしても気になる人は入れてみては?。

侵略するには

Screenshot_1578906820.png

こんなふうに上にも表示させたい場合は、

//ステータスバー透明化+タイトルバー非表示+ノッチ領域にも侵略
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を非表示にしてますが、どっかでそもそも表示しない設定にしている場合は
二行目を消したほうがいいと思います。

これでノッチ領域に侵略することに成功しました。

Screenshot_1578906694.png

以上です。お疲れさまでした。888888888

参考にしました

https://stackoverflow.com/questions/49190381/fullscreen-app-with-displaycutout

android display cutout full screen って検索したんですけどGoogle先生すごい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.Helpers

Formの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というファイルを置いて開いてみました)
intent.png
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.getDatanilかどうかで判定できます。

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;

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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ハイパーバイザープラットフォームにチェックを入れます。

Windows の機能 2020_01_13 11_42_58.png

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つ目)を起動します

image.png

  • 仮想デバイスを作成して、Launch this AVD in the emulatorで起動します

Android Virtual Device Manager 2020_01_13 13_24_33.png

  • このような感じで起動します

Android Emulator - Nexus_5_API_29_5554 2020_01_13 13_27_35.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android 動画再生アプリ開発(ExoPlayer)

Android Exoplayerの使い方

Exoplayerとは簡単に言うとAndroidで動画再生をするためのAPIをたくさん用意したライブラリです。

通常1から動画プレイヤーを開発しようとすると、動画デコード、音声デコード、ストリーミング、復号化などを全て作らなければなりませんが、Exoplayerでは簡単なAPIですぐに実装できます。

API 16以上のSDKであれば使用できるのでハードウェアにも依存せず使用できます。

[下記公式URL]
ExoPlayer | Android デベロッパー | Android Developers

Exoplayer導入

初めにExoplayerライブラリをプロジェクトに追加します。

google(), jcenter()追加

まず、build.gradleに下記を追加します。

build.gradle
repositories {
    google()
    jcenter()
}

Exoplayerライブラリ追加

次にExoplayerのライブラリを追加するのですが、Exoplayerはいくつかのライブラリを分けていますが、一括で導入したい場合は下記です。
2020/01/13時点での最新バージョンは2.11.1

build.gradle
implementation 'com.google.android.exoplayer:exoplayer:2.11.1'

特定のライブラリのみを追加したい場合、Exoplayerは5つのライブラリに分かれているので、これを指定してください。
Exoplayer

  • core
    • 動画配信用のコアライブラリ
  • dash
    • Dashコンテンツ用ライブラリ
  • hls
    • Hlsコンテンツ用ライブラリ
  • smoothstreaming
    • smmothstreaming用ライブラリ
  • ui
    • UIカスタム用のライブラリ

例えば、coreのみを指定する場合は下記です。
ライブラリを分けることはapkサイズの縮小に繋がります。

build.gradle
implementation 'com.google.android.exoplayer:exoplayer-core:2.11.1'

Java 8追加

build.gradle
compileOptions {
  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などでお知らせください。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidでも Markdown + PlantUML したい

Androidでも Markdown + PlantUML したい

注記:残念ながら日本語入力ができなくなっていました。現在調査中のため、以下の手順は実施しないでください。

今の所、私の探した範囲では Markdown + PlantUML が書けるAndroidアプリは存在しないっぽい(個別には存在する)ので、Linux(Debian)上で Visual Studio Code と Graphviz をインストールして書けるようにしてみました。
Linux(Debian)を構築していない人は、事前にここを見てDebianの環境を設定してください。

Graphviz のインストール

Graphviz は文字列を基に図を書くためのツールです。
その図の中に PlantUML も入っていると思ってください。
なお、Graphviz は Java の上で動くので Java のインストールも必要です。
一緒にインストールします。

  1. デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > System Tools > File Manager PCManFM
  2. /data/data/tech.ula/files/1/etc/apt/ までディレクトリを移動する
  3. sources.list を右クリックして(あるいはタッチパネルで二本指でタップ)コンテキストメニューから Mousepad を選択する
  4. 以下を適当な位置(私な一番下の位置にした)に入力して保存する
deb http://ftp.de.debian.org/debian buster main
deb http://ftp.jaist.ac.jp/debian buster main
  1. デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > Debian > Applications > Shells > Bash
  2. 次に以下のコマンドを 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 install

Visual 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_class.png

PlantUML の書き方は以下のサイトで確認してください。

https://plantuml.com/ja/

参考サイト

64-bit ARMv8 マシン用 graphviz_2.40.1-6_arm64.deb のダウンロードページ

Chromebook C101PAでVSCodeを使う。(Crostini使用)

headmelted / codebuilds

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidでも Markdown + PlantUML したい(暫定対応)

Androidでも Markdown + PlantUML したい

注記:残念ながら日本語入力ができなくなっていました。現在調査中のため、以下の手順は実施しないでください。
→ 暫定対応で「デスクトップ環境を日本語化」を行わないことにしました。とりあえず使えますが、英語の環境になります。

今の所、私の探した範囲では Markdown + PlantUML が書けるAndroidアプリは存在しないっぽい(個別には存在する)ので、Linux(Debian)上で Visual Studio Code と Graphviz をインストールして書けるようにしてみました。
Linux(Debian)を構築していない人は、事前にここを見てDebianの環境を設定してください。

Graphviz のインストール

Graphviz は文字列を基に図を書くためのツールです。
その図の中に PlantUML も入っていると思ってください。
なお、Graphviz は Java の上で動くので Java のインストールも必要です。
一緒にインストールします。

  1. デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > System Tools > File Manager PCManFM
  2. /data/data/tech.ula/files/1/etc/apt/ までディレクトリを移動する
  3. sources.list を右クリックして(あるいはタッチパネルで二本指でタップ)コンテキストメニューから Mousepad を選択する
  4. 以下を適当な位置(私な一番下の位置にした)に入力して保存する
deb http://ftp.de.debian.org/debian buster main
  1. デスクトップ画面の一番左下の鳥(?)のようなマークのアイコンをクリック > Debian > Applications > Shells > Bash
  2. 次に以下のコマンドを Bash に入力して実行する。
sudo apt update
sudo apt upgrade
sudo apt install openjdk-11-jdk
sudo apt install graphviz

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 install

Visual 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_class.png

PlantUML の書き方は以下のサイトで確認してください。

https://plantuml.com/ja/

参考サイト

64-bit ARMv8 マシン用 graphviz_2.40.1-6_arm64.deb のダウンロードページ

Chromebook C101PAでVSCodeを使う。(Crostini使用)

headmelted / codebuilds

(やらないで!)デスクトップ環境を日本語化

残念ながら、task-japanese-desktop をインストールすると Anthy が使えなくなるところまで分かりました。
そこから日本語入力を使えるようにするところまで調査を終えておりません。


Bash で以下のコマンドを実行しデスクトップ環境を日本語化します。

sudo apt install task-japanese
sudo apt install task-japanese-desktop
sudo dpkg-reconfigure tzdata
sudo dpkg-reconfigure locales

設定後は再起動してください。


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

implementationとapiの違いメモ

implementationapi

コンフィグレーション 依存関係の伝播
implementation しない
api する

サンプル

image.png

このとき、プロジェクトAから見て

プロジェクト 利用可否
B 使える
C 使える
D 使えない
E 使える
F 使えない
G 使える

となるはず。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gradleのimplementationとapiの違いメモ

implementationapi

コンフィグレーション 依存関係の伝播
implementation しない
api する

サンプル

image.png

このとき、プロジェクトAから見て

プロジェクト 利用可否
B 使える
C 使える
D 使えない
E 使える
F 使えない
G 使える

となるはず。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む