- 投稿日:2019-01-27T22:32:19+09:00
[Unity]Android Studio 3.1.2 によるAndroidネイティブプラグイン作成
Androidネイティブプラグインの作り方
ネットを検索すればプラグインの作り方について書かれたサイトは沢山見つかりますが、どれも古い情報だったためか、すんなりと作成できなかったので、簡単にまとめてみます。
UnityPlayerActivityを継承したクラスを使ったプラグイン
ここにあるように、プラグインの作り方にはいくつか方法があるのですが、今回はUnityPlayerActivityを継承したクラスを使う方法について書いていきます。
- Android Studio のプロジェクトを新規作成して、Android Libraryモジュールを追加
- Unityのclasses.jarをlibsにコピー(classes.jarの場所は公式ドキュメントを参照)
- UnityPlayerActivityを継承したjavaクラスを追加
- プラグインにclasses.jarを含めないようにするため、gradleに
android.libraryVariants.all { variant -> variant.outputs.each { output -> output.packageLibrary.exclude('libs/classes.jar') } }を追加
Unityでビルドしようとするとclasses.jarのモジュールが競合してしまう
検索すると、大体上記の方法にたどり着くのですが、手順4でclasses.jarを除外することができません。(おそらく、gradleのバージョンが上がって、dependenciesに
implementation fileTree(dir: 'libs', include: ['*.jar'])と記述されているからだと思われます。以前はcompile~とかだったみたい。正しい情報を知りたい方はGradleの公式ドキュメントを見てください。)色々調べた結果、implementation用のディレクトリの他にコンパイル時だけ参照するライブラリ用のディレクトリを追加し、そこにclasses.jarを置きます。そして、depencenciesにcompileOnlyでそのディレクトリを指定してやることで、classes.jarを含まないaarを出力することができました。
- Android Studio のプロジェクトを新規作成して、Android Libraryモジュールを追加
- compile_onlyディレクトリを新規作成し、Unityのclasses.jarをそこに配置
- UnityPlayerActivityを継承したjavaクラスを追加
- gradleのdependenciesに
compileOnly fileTree(dir: 'compile_only', include: ['*.jar'])を追加例
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) compileOnly fileTree(dir: 'compile_only', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }まとめ
classes.jarはcompileOnlyで追加する!
他の細かい手順は省略・・・(検索すれば大体わかるはず)
- 投稿日:2019-01-27T09:31:53+09:00
Dart x Flutter : RenderObject を StatelessWidget 上に表示させてみる
StatelessWidget と StateWidget は、よく話題にあがりますが、
RenderObjectWidget は、話題上がらないでの、補完します。英語圏でも情報がないので、そんなものとして、扱ってください
こんな感じで、3Dっぽい表現を、Flutterアプリに簡単に追加できます。
コード
https://github.com/kyorohiro/memo_flutter_2019四角形を表示するRenderObject を StatelessWidget に 貼り付ける
import 'package:flutter/material.dart' as ma; import 'package:flutter/material.dart' as sky; import 'package:flutter/widgets.dart' as sky; import 'package:flutter/rendering.dart' as sky; import 'package:vector_math/vector_math_64.dart' as vec; import 'dart:async'; main() async{ await new Future.delayed(Duration(seconds: 2)); ma.runApp(MyApp()); } class MyApp extends ma.StatelessWidget { // This widget is the root of your application. @override ma.Widget build(ma.BuildContext context) { return ma.MaterialApp( title: 'Flutter Demo', theme: ma.ThemeData( primarySwatch: ma.Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends ma.StatelessWidget { @override ma.Widget build(ma.BuildContext context) { return new ma.Scaffold( appBar: ma.AppBar(title: ma.Text("Hello")), body: createBody(context), ); } } ma.Widget createBody(ma.BuildContext context) { // return DrawRectWidget(); return ma.Row(children: <ma.Widget>[ ma.Text("Hello"), DrawRectWidget(), ma.Text("Render"), ],); } // // Rect // class DrawRectWidget extends sky.SingleChildRenderObjectWidget { sky.RenderObject createRenderObject(sky.BuildContext context){ return new DrawRectObject(); } } class DrawRectObject extends sky.RenderBox { @override bool hitTestSelf(sky.Offset position) => true; @override void performLayout() { this.size = sky.Size(50,50); } @override void handleEvent(sky.PointerEvent event, sky.BoxHitTestEntry entry) {} void paint(sky.PaintingContext context, sky.Offset offset) { print("${offset} ${this.size}"); sky.Paint p = new sky.Paint(); context.canvas.transform(vec.Matrix4.translation(vec.Vector3(offset.dx,offset.dy, 1.0)).storage); p.color = new sky.Color.fromARGB(0xff, 0x55, 0x55, 0x55); sky.Rect r = new sky.Rect.fromLTWH(0.0, 0.0, 50.0, 50.0); context.canvas.drawRect(r, p); context.canvas.transform(vec.Matrix4.translation(vec.Vector3(-offset.dx,-offset.dy, 1.0)).storage); } }
- 投稿日:2019-01-27T08:03:16+09:00
dagger.androidでActivityからModuleへ値を渡す
はじめに
dagger.androidを使用すると、
AndroidInjection.inject(this)というようにActivity側は簡潔な記述になりますが、古いDaggerの実装で
(application as MyApplication).getAppComponent() .mainActivityBuilder(MainActivityModule("hoge")) .build() .inject(this)このようにActivityからModuleに何かしらの値を渡していると、dagger.androidに移行するときに同じような書き方ができなくて辛いです。
しかし、どうしてもActivityからModuleへ値を渡す必要がある、そんな状況に陥ってしまったので色々調べながら実装しました。どうやるか
公式サイトの
Injecting Activity objectsを参考にMainActivity用のSubcomponentを作成します。
1箇所だけ公式と違う実装があります。MainActivitySubcomponent.kt@Subcomponent(modules = [MainActivityModule::class]) interface MainActivitySubcomponent: AndroidInjector<MainActivity> { @Subcomponent.Builder abstract class Builder : AndroidInjector.Builder<MainActivity>() { abstract fun mainActivityModule(module: MainActivityModule): Builder override fun seedInstance(instance: MainActivity?) { mainActivityModule(MainActivityModule(instance!!.prefix)) } } }作成したSubcomponentをActivityModuleに紐付けます。
ActivityModule.kt@Module(subcomponents = [MainActivitySubcomponent::class]) interface ActivityModule { @Binds @IntoMap @ClassKey(MainActivity::class) fun bindMainActivity(builder: MainActivitySubcomponent.Builder): AndroidInjector.Factory<*> }AcitivityModuleはAppComponentのmodulesに定義します。
AppComponent.kt@Component(modules = [AndroidInjectionModule::class, AppModule::class, ActivityModule::class]) interface AppComponent { fun inject(app: App) }特筆すべきはMainActivitySubcomponentのSubcomponent.Builder内にあるseedInstanceメソッドです。
seedInstanceメソッドはMainActivityを引数に持っているので、MainActivityのpublicなメンバ変数やメソッドを準備してあげれば参照できます。
MainActivity側は下記のようになっています。MainActivity.ktclass MainActivity : AppCompatActivity() { @Inject lateinit var injectionClassA: InjectionClassA @Inject lateinit var injectionClassB: InjectionClassB public val prefix = "Android" override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val textView = findViewById<TextView>(R.id.text_view) textView.setText("${injectionClassA.getHello()} ${injectionClassB.getDagger()} !") } }今回は、prefixというメンバ変数を準備してseedInstanceメソッド内でModuleに渡しています。
注意点として、AndroidInjection.inject(this)より前に渡す値を定義してあげないといけないです。所感
MainActivityがModuleのことを知らなくてよくなったと思ったら、ModuleがMainActivityのメンバ変数を知ってる状態に変わっただけというオチですね。
一時回避策としてはありかもしれませんが、最終的にはModuleへ値を渡す必要がないようにModule内でnewするクラスのコンストラクタを見直したほうがよいかと思いました。
コードが一部だけだとわかりづらいのでGitHubにあげます。
- 投稿日:2019-01-27T06:31:45+09:00
指紋認証のアプリ組み込み時の懸念点
①内容
指紋認証をアプリに組み込む際に懸念していることをつらつらと書いてみました
(あくまで個人的な考えです)②アプリへの実装方式
Androidの場合
Android Marshmallow (Android 6.0.0)以上の場合はFingerPrint、
Android Pie (Android 9.0.0)以上の場合はBiometricPromptを使うかと思います
上記以外は指紋認証センサー(Nexus Imprintセンサー)がないので諦めるiOSの場合
iPhone 5s以降でiOS 8.0 以上のiPhoneの場合はLocalAuthenticationを使ってTouch IDを呼び出す
上記以外のバージョンはtouch IDが無いので諦める
iPhone X・iPhone XS・iPhone XRの場合もtouch IDが無いので諦めるかFace IDで対応するハイブリッドの場合
Cordova(Monaca)で想定しています。
cordova-plugin-keychain-touch-idをインストールして、使用する。
なお、サードパーティ製APIなので、Proプランへの登録が必要
(サードパーティ製なので、問題なく開発時に受け入れられるかという点も考慮する)
この場合も当然ですが、端末に機能が無ければ無理なので、注意する③設計時の考慮
社内で展開して同じ端末しか使わない、修理は正規の業者に依頼する場合には不要の観点です。
特定の端末は動作対象外としてアプリを開発・登録する、別方法の認証を用意するなどで回避する必要があると思います
端末のバージョンにより指紋認証ができない場合を考慮する
②に書いた通り、端末が対応していなければお話にならない端末に不具合が生じて指紋認証ができない場合を考慮する
例えば、非正規の店で画面修理をした場合です。
iPhoneやiPadではiOSのアップデートやバックアップ復元時に他の部品が正規のものであるかチェックして、非正規以外の物があればブート時にエラー53と出て、Touch IDが利用できなくなります。
(ちなみに私の実例です。。。)④まとめ
アプリを開発する時には対象OS・バージョンなどを考慮する必要があるのは当然ですが、ユーザの不手際により一部機能が使用できなくなった時のことも考える必要があると思いました。
最近アプリ開発を始めてみましたが、バージョンの考慮が本当に大変ですね。。。

