20190127のAndroidに関する記事は4件です。

[Unity]Android Studio 3.1.2 によるAndroidネイティブプラグイン作成

Androidネイティブプラグインの作り方

ネットを検索すればプラグインの作り方について書かれたサイトは沢山見つかりますが、どれも古い情報だったためか、すんなりと作成できなかったので、簡単にまとめてみます。

UnityPlayerActivityを継承したクラスを使ったプラグイン

ここにあるように、プラグインの作り方にはいくつか方法があるのですが、今回はUnityPlayerActivityを継承したクラスを使う方法について書いていきます。

  1. Android Studio のプロジェクトを新規作成して、Android Libraryモジュールを追加
  2. Unityのclasses.jarをlibsにコピー(classes.jarの場所は公式ドキュメントを参照)
  3. UnityPlayerActivityを継承したjavaクラスを追加
  4. プラグインに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を出力することができました。

  1. Android Studio のプロジェクトを新規作成して、Android Libraryモジュールを追加
  2. compile_onlyディレクトリを新規作成し、Unityのclasses.jarをそこに配置
  3. UnityPlayerActivityを継承したjavaクラスを追加
  4. 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で追加する!
他の細かい手順は省略・・・(検索すれば大体わかるはず)

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

Dart x Flutter : RenderObject を StatelessWidget 上に表示させてみる

StatelessWidget と StateWidget は、よく話題にあがりますが、
RenderObjectWidget は、話題上がらないでの、補完します。

英語圏でも情報がないので、そんなものとして、扱ってください

ezgif-3-99636bd7e2fd.gif

こんな感じで、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-27 9.30.08.png

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

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.kt
class 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にあげます。

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

指紋認証のアプリ組み込み時の懸念点

①内容

指紋認証をアプリに組み込む際に懸念していることをつらつらと書いてみました
(あくまで個人的な考えです)

②アプリへの実装方式

  1. Androidの場合
    Android Marshmallow (Android 6.0.0)以上の場合はFingerPrint、
    Android Pie (Android 9.0.0)以上の場合はBiometricPromptを使うかと思います
    上記以外は指紋認証センサー(Nexus Imprintセンサー)がないので諦める

  2. iOSの場合
    iPhone 5s以降でiOS 8.0 以上のiPhoneの場合はLocalAuthenticationを使ってTouch IDを呼び出す
    上記以外のバージョンはtouch IDが無いので諦める
    iPhone X・iPhone XS・iPhone XRの場合もtouch IDが無いので諦めるかFace IDで対応する

  3. ハイブリッドの場合
    Cordova(Monaca)で想定しています。
    cordova-plugin-keychain-touch-idをインストールして、使用する。
    なお、サードパーティ製APIなので、Proプランへの登録が必要
    (サードパーティ製なので、問題なく開発時に受け入れられるかという点も考慮する)
    この場合も当然ですが、端末に機能が無ければ無理なので、注意する

③設計時の考慮

社内で展開して同じ端末しか使わない、修理は正規の業者に依頼する場合には不要の観点です。
特定の端末は動作対象外としてアプリを開発・登録する、別方法の認証を用意するなどで回避する必要があると思います

  1. 端末のバージョンにより指紋認証ができない場合を考慮する
    ②に書いた通り、端末が対応していなければお話にならない

  2. 端末に不具合が生じて指紋認証ができない場合を考慮する
    例えば、非正規の店で画面修理をした場合です。
    iPhoneやiPadではiOSのアップデートやバックアップ復元時に他の部品が正規のものであるかチェックして、非正規以外の物があればブート時にエラー53と出て、Touch IDが利用できなくなります。
    (ちなみに私の実例です。。。)

④まとめ

アプリを開発する時には対象OS・バージョンなどを考慮する必要があるのは当然ですが、ユーザの不手際により一部機能が使用できなくなった時のことも考える必要があると思いました。

最近アプリ開発を始めてみましたが、バージョンの考慮が本当に大変ですね。。。

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