t_t_nの日記

開発の備忘録など

permission 覚え書き

こういうコード断片を適宜残すのは大切だと感じた

インタフェース

interface RuntimePermissionHandler<T> {

	companion object {
		const val REQUEST_CAMERA = 1
	}

	var controller: T?
	var onPermissionOk: (() -> Unit)?
	var onPermissionNg: ((message: String?) -> Unit)?

	fun requestPermission()
	fun handlePermissionResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)
}

実装

class CameraPermissionHandler(override var controller: Activity?, override var onPermissionOk: (() -> Unit)?) : RuntimePermissionHandler<Activity> {
	override var onPermissionNg: ((message: String?) -> Unit)? = null

	override fun requestPermission() {
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
			onPermissionOk?.invoke()
			return
		}
		controller?.let {
			if (ContextCompat.checkSelfPermission(it, CAMERA) == PackageManager.PERMISSION_GRANTED) {
				onPermissionOk?.invoke()
				return
			}
			ActivityCompat.requestPermissions(it, arrayOf(CAMERA), RuntimePermissionHandler.REQUEST_CAMERA)
		}
	}

	override fun handlePermissionResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
		if (requestCode == RuntimePermissionHandler.REQUEST_CAMERA && grantResults.isNotEmpty()) {
			if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
				onPermissionOk?.invoke()
			} else {
				onPermissionNg?.invoke("fail!!!!!!!!!!")
			}
		}
	}

}

呼ぶ

class MainActivity : AppCompatActivity() {

	private var mCameraPermissionHandler: RuntimePermissionHandler<Activity>? = null

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)
		mCameraPermissionHandler = CameraPermissionHandler(this, { init() })
		mCameraPermissionHandler?.onPermissionNg = {
			message ->
			message?.let {
				Snackbar.make(root, it, Snackbar.LENGTH_SHORT).show()
			}
		}
		mCameraPermissionHandler?.requestPermission()
	}

	override fun onDestroy() {
		super.onDestroy()
		mCameraPermissionHandler = null
	}

	override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
		super.onRequestPermissionsResult(requestCode, permissions, grantResults)
		mCameraPermissionHandler?.handlePermissionResult(requestCode, permissions, grantResults)
	}

	private fun init() {
		val s = Snackbar.make(root, "init!", Snackbar.LENGTH_LONG)
		s.setAction("reInit", {
			v ->
			val ss = Snackbar.make(v, "reinit!!!!!", Snackbar.LENGTH_SHORT)
			ss.show()
		})
		s.show()
	}

}

使ってみたら、イマイチな箇所があったので更新・・。
shouldShowRequestPermissionRationaleの使い方がいまいちピンと来ない・・。

Espressoの初期設定がうまくできない時は

f:id:shsato_t_t_n:20180221234143p:plain

結構悩んだので備忘録として残しておきます。
単純なことだったのですが、kotlinが原因だと思い込んであれこれ無駄に調べ回っては
古い情報を試してうんうん悩んでしまいました。

通常通りAndroidStudioでプロジェクトを作成した場合、プロジェクトの設定などを
追加で書き換えたりする必要は現状無いようです。

では何をしなければならないのかと言うと
テストのConfigをjUnitではなく、Android Instrumented Tests で作成する必要がありました。
(わかってみればそりゃそうだって感じですが)

悩みました・・。

〜に依存している

ずっと、「〜に依存している」というワードの使い方がわからなかったのですが、
最近、クリーンアーキテクチャについて学ぶ中で、こういうことかーと思ったので喜びを書きます。

僕がこのワードの使い方がわからなかった理由は、「〜に依存している」と言う時に、
具体的に何を指すのかが文脈によってころころ変わるからだったようです。
ある時にわかったと思っても、違う時には全然違うものを指したりするから、
あれ?・・となる。

クリーンアーキテクチャで例の玉ねぎに沿ったパッケージ構成を作成し、そのパッケージ間を
つなぐインタフェース役のクラスを作成した時に唐突に理解しました。

つまり、Javaで言えば、
「AクラスがBクラスをimportしている場合、AクラスはBクラスに依存している」と言えるのだなと。

これまで、処理の分割など自分の中でルールがあまりなく、ふわふわでそれらしく実装していたのですが、
これからはきちんと根拠を持って出来そうです。すごく嬉しい。
勝手に増えるやつくらいにしか思っていなかったimportの大事さが分かりました。

それに、慣れない言語で書く時なんかに一つの指針とできそうなのも嬉しいです。
残業続きの中、思わぬ幸運を拾った思いです。

Kotlinでクラスのnameを取得するには

Android開発の際、ログを出す時やfragmentのtagによくクラス名を使う。
kotlinでも同じようにしようと思ったのだけど、下記のようにすると

MainActivity::javaClass.name

単に「javaClass」とログに出てきて悲しい感じに・・

MainActivity::class.java.name

とすると期待した結果になった。
下の書き方だと、最終的にClass.javaのgetNameにたどり着く。

WebViewでリダイレクトかどうかを判定する

判定する。

リダイレクトの際にもshouldoverrideurlloadingの中に入ってくるので、
そこは無視したい。

WebView.HitTestResultのUNKNOWN_TYPEの場合はリダイレクトだとする方法で当初は上手くいって見えた。

しかし、リンクテキストをタップした時なんかにもたまにこれが来る時があった。
SRC_ANCHOR_TYPEが通常来るが、たまにUNKNOWN_TYPEで入ってくるという具合。

そのためこの方法は使えず。

色々と調べた結果、リダイレクトの時はonPageFinishedより先にshouldoverrideurlloadingに
入ってくることが判明。

stackoverflow.com

ここで教えてくれている通りにフラグで管理するようにして解決した。
ありがたい・・。

droidkaigiいってきた

行ってきた

仕事の合間に行ったので対して聞けなかったけれども、結構勉強になりました。
英語のスピーチを試しに聞いてみたけど辛いだけだった。英語勉強しなきゃ()

今回は聞いた内の一つ、「実践アニメーション」のサンプルを読む。

xmlで複雑なアニメーションは聖域になるからやめた方が良い、ってのが一番印象に残ってる。

Androidのアニメーションの基本部分の簡単なサンプルたちはスルーして、
花火してたところを見る。

MarbleAnimationってところかなぁ多分


まずは呼び出し側
MarbleAnimationActivity.java

animatorView = (MarbleView) findViewById(R.id.animatorView);

        animatorView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animatorView.startCustomAnimation();
            }
        });

ほんとに呼ぶだけって感じ。カスタムのView + ValueAnimator最強って言ってたから、
その通りの作りになってるんだな、って。


というわけで本体
MarbleView.java

private void initParams() {
        maxDistance = getContext().getResources().getDimensionPixelSize(R.dimen.marble_max_distance);
        circleRadius = getContext().getResources().getDimensionPixelSize(R.dimen.marble_max_circle_radius);
    }

Viewの拡張クラスの中でgetContextって出来るんだ・・!(無知)
コンストラクタで必ず渡してるんだから当たり前かぁ

上記の個所は初期値を定義している部分で、すべてのコンストラクタで呼び出していた。
外から渡したりはしないんすねーってちょっと思ったけど、設定値を書き換えろやって話か

// 外の公開しているのはこのメソッドだけみたい
 public void startCustomAnimation() {
        if (isRunning) return;

        isRunning = true; // このフラグを折ってるところが見当たらない
        for (int i = 0; i < animators.length; i++) { // animatorsはValueAnimatorの配列で20個
            createAndStart(i);
        }
    }


20個のValueAnimatorを作成してるみたい。
花火の各点になるのかな?なるんじゃないかな?

private ValueAnimator createAnimator() {
        Random random = new Random();
        ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
                PropertyValuesHolder.ofFloat(VELOCITY_X, (random.nextFloat() - 0.5f) * 2),
                PropertyValuesHolder.ofFloat(VELOCITY_Y, (random.nextFloat() - 0.5f) * 2),
                PropertyValuesHolder.ofFloat(PROGRESS, 1.0f));
        animator.setDuration(random.nextInt(MAX_DURATION / 2) + MAX_DURATION / 2);
        animator.setInterpolator(new DecelerateInterpolator()); // 最初速くて減速する。花火だ。
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                invalidate(); // これを使ってonDrawを呼び出している様子
            }
        });
        return animator;
    }

一番知りたかった部分・・・かな?
valueAnimatorで値が変化するとonAnimationUpdateが呼ばれるみたい。
そこでinvalidateすることでonDrawを呼んでいる。

setInterpolatorって何だろうと思って調べたけど、スライドにちゃんと書いてた。

private void drawPhotons(Canvas canvas) {
        for (int i = 0; i < animators.length; i++) {
            float velocityX = (float) animators[i].getAnimatedValue(VELOCITY_X);
            float velocityY = (float) animators[i].getAnimatedValue(VELOCITY_Y);
            float progress = (float) animators[i].getAnimatedValue(PROGRESS);
            paints[i].setAlpha(255 - (int) (255 * progress));
            canvas.drawCircle(maxDistance * velocityX + centerOfX(),
                    maxDistance * velocityY + centerOfY(),
                    circleRadius * progress,
                    paints[i]);
        }
    }


最後、onDraw内で呼んでいるメソッド。
ValueAnimatorで動きを定義して、ここで実際の描画を行っているっぽい。
色はcreatePaintメソッドの方でランダムで定義してた。

drawCircleで行っている計算は速度をうまく使ってやってるみたい。
つまり、ランダムで発生した速度を点のサイズに反映することで、速い点はでっかく、
小さい点はちっこくなる・・んじゃないかなぁ。

progressって何だろう・・・

xmlで書くよりは確かに読みやすそう。でも描画を行っている部分が多少複雑になりそう。

これでAndroidのアニメーションが書けるようになった!

AndroidThingsのサンプルを読んでみる

読んでみる。
対象になるサンプルコードはいくつかあるが、今回はBlinkActivityにする。

(前回このコードでLED光らせたし・・。)


BoardDefaults.java

・・・動いているデバイスが何かを判定してるっぽい?。
edisonってデバイスの時だけ色々してるから、Android的には同じデバイスって判断しちゃうけど
実際はGPIOのところの配置が違う別物って感じなのかな。

if (sBoardVariant.equals(DEVICE_EDISON)) {
            PeripheralManagerService pioService = new PeripheralManagerService();
            List<String> gpioList = pioService.getGpioList();
            if (gpioList.size() != 0) {
                String pin = gpioList.get(0);
                if (pin.startsWith("IO")) { // 0番目のピン?が"IO"で始まる何かならこっち
                    sBoardVariant = DEVICE_EDISON_ARDUINO;
                }
            }
        }


大事なのはgetGPIOForLED()のところっぽい。
バイスごとに違う文字列返している。
LEDをセットしているところがどこか教えろーって感じかー


BlinkActivity.java

全然関係ないけど、Handlerはフィールドで宣言して初期化してるのね。
毎回newしてたけどこっちの書き方がいいのかねぇ。
中身読まなきゃなぁ。



mBlinkRunnable がフィールドにいない・・・!と思ったら下で宣言してた。
自由だな!

// このサービスにGPIOの名前を文字列で渡すといいみたい
PeripheralManagerService service = new PeripheralManagerService();

// 大事そう。DIRECTION_OUTとDIRECTION_INがあるらしい
// OUT_~以降はよくわからん。
mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);


// このsetValueに値を渡してon/offを切り替えている。
// 今回はLEDを点滅させるためにこうしてるみたい。
mLedGpio.setValue(!mLedGpio.getValue());


GpioインスタンスにどこにつなげるかとIN/OUTを教えてあげて、最後にon/offを
切り替えるって感じかな。
切り替え処理はHandlerに渡す形で実装しないとダメみたいだ。

closeを忘れるとまずそうだ。


センサーとかだと結果の戻りがあると思うんだけど、どう処理するんだろう。
それはボタンのサンプル見れば分かるかな?次回はそちらを動かしてみよう。


removeCallbacksってなんだろ。