パルカワ2

最近はFlutterをやっています

おはぎレビュー #2

我々の世界では、人間が作ったおはぎをうんこでないかレビューすることを「おはぎレビュー」と言う。

ohagi day #12

おはぎを作っていたつもりがうんこを作ってたなんて事は人間であればよくある。それは大した問題ではなく問題なのは、うんこをおはぎとして客に出し、客がうんこを食べてしまうことである。他にもおはぎレビューを行う理由はあるが、我々がおはぎレビューを行う理由の1つがそれだ。

毎日のようにおはぎのようなうんこを作り、うんこのようなおはぎを作ってしまう不完全な人間である我々にとって、おはぎレビューはなくてはならない存在だ。
なくてはならない存在だからこそ、我々はおはぎレビューについて考え、議論し、学び、より良くしていく必要がある。

「みんなではじめるデザイン批評」という本がある。その本の「批評を理解する」という章には以下のように書かれている。これは、おはぎレビューも同様だと考える。

私たちは全員がスキルやノウハウを持ち寄って、協力し合う必要があるのだ。そしてそのためには、互いに話をしなければならない。何をデザインしているのか、なぜそれを作っているのか、どのように完成させていくのかを議論しなければならないのである。

みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド

みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド

おはぎレビューでは、レビュアーとレビュイーがお互いが理解し合う努力が必要であると3年前に書いた。それはおはぎレビューが、レビュアーの指摘によってうんこからおはぎにするのではなく、レビュアーとレビュイーの会話によって、うんこからおはぎに近づけるプロセスであるからだ。

おはぎレビューにおける会話とは、以下のようなものではない。

レビュアー「ここはAが起きるとつらいのでBに直したほうがいいと思います」
レビュイー「修正します」
レビュイー「修正したので確認お願いします」

この場合だとレビュアーの考えしか出ておらず、レビュアーの指摘によってうんこからおはぎにするプロセスと化している。これだとおはぎがうんこになってしまう可能性がある。

おはぎレビューでは、レビュイーが自分の考えを積極的に伝えていく必要がある。

レビュイー「ここはAという理由からBにしました」
レビュアー「なるほど、BだとCになってしまうのでDが良いかと思いましたがどうですか?」
レビュイー「Cに気づいてませんでした。それを考えるとDが良いと思いました!そうします!」

このようにおはぎレビューにおける会話とは、お互いが自分の考えを相手に伝えることである。そうすることで「そうだったのか!」「知らなかった!」を見つけて、うんこからおはぎに近づけていく。

レビュイーとしての自分が最近気をつけているのは、レビュイーの自分から会話を始めるということだ。自分がなぜこの形のおはぎにしたのか・この味にしたのか・また悩んでる事をレビュアーが知る事で、より会話が生まれやすい。
また、言語化することで、自分の考えもまとまるし、コメントとして残す事で後日レビューを見た時に何を考えておはぎを作ったのかわかって良いことだらけだ。

まとめ

  • レビュイーとレビュアーは自分の考えを伝え、相手の考えを聞く事を繰り返す事で、お互い理解しあえて、うんこがおはぎに近づいていく。
  • レビュイーは自分から積極的に会話を促すと良い

ohagi day #16

ここで利用させて頂いた全ての画像は、正常なおはぎです。

FILCO ウッドパームレスト買った

前使ってたゴムみたいなやつがボロボロになったので、家用と会社用に買った。
木なので冷たいのかなと思ったけどそんな事なかった。なんかさわり心地もいいし、もっと早く買っとけばよかった。

ユリゴコロを見た

yurigokoro-movie.jp


丸の内TOEIまで行った










まとめ

吉高由里子は可愛い

minneをAndroid Studio 3.0に対応させた

hisaichi5518.hatenablog.jp

↑の続きです。
Android Studio Release Notes | Android Studio
Android Developers Blog: Android Studio 3.0

com.android.tools.build:gradle:3.0.0 を使う

まあ、そうですよね

assertThat(images).isNotNull が使えなくなったのでやめた

minneではテストでKotlinとassertjを利用しているんですが、 .isNotNull()ではなく .isNotNullと書けてた。一応.isNotNull()を利用するようにしましょうという感じだったんだけど、残念なことにいくつか漏れがあってAndroid Studio 3.0にしたタイミングで.isNotNullコンパイルができなくなったので、修正した。

testCompile "org.robolectric:httpclient:3.4.2"

まあまあ昔からあるアプリなので、 org.apache.http.NameValuePairに依存してたりする。今まではエラーになってなかったけど、Android Studio 3.0にしたタイミングでテストでClassNotFoundExceptionを吐くようになったので、robolectricのhttpclientを追加した。

このあたりは、消したい気持ちをこらえる必要がありました。

Annotation Processorがオフになってるよと言ってきたのでONにした

Android Studioが警告を出してきたのでポチポチした。
前も出てたけど、クリックしても設定画面に飛ばなかったんですが、今回は設定画面に飛んで設定出来るようになりました。

まとめ

というわけで、minneを運営するGMOペパボは本日めでたくリリースされたAndroid Studio 3.0 stable対応企業です。よろしくお願いいたします。
【minne】Androidアプリエンジニア / GMOペパボ株式会社

ActivityOptionsCompat.makeSceneTransitionAnimation(...) を使う

Activity間で共通の画像があって、それをシューンと移動させるのがマテリアルしてんじゃん!という感じなのでやってみた。
試したのは、エミュレータAndroid 7.0です。全部のコードはここにある。

github.com

f:id:hisaichi5518:20170927234954g:plain

このような感じに動くシンプルなやつ

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        GlideApp.with(this)
                .load(R.drawable.sample)
                .into(binding.view1);

        RxView.clicks(binding.view1).subscribe(__ -> {
            ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                    this, binding.view1, "image");

            ContextCompat.startActivity(this, new NextActivity.IntentBuilder(this).build(), options.toBundle());
        });
    }
}

遷移先のActivity

public class NextActivity extends AppCompatActivity {

    private ActivityNextBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_next);

        GlideApp.with(this)
                .load(R.drawable.sample)
                .into(binding.view1);

        RxView.clicks(binding.view1).subscribe(__ -> {
            ActivityCompat.finishAfterTransition(this);
        });
    }


    static class IntentBuilder {
        private final Context context;

        IntentBuilder(Context context) {
            this.context = context;
        }

        Intent build() {
            return new Intent(context, NextActivity.class);
        }
    }
}

あとは遷移先のImageViewにandroid:transitionName="image"を指定してあげると動く。
案外簡単だった。windowに値をセットして〜〜〜とか説明してる記事がいくつかあったけど、特にいらなかった。試したのがAndroid 7.0だからかもしれない。またtransitionNameはstrings.xmlとかに書くのが本当はいいと思う。

時々変に移動する時がある。これはよくわからなかった。

テストでAnnotation Processorを利用する

testAnnotationProcessor "..."

と書くと実行されてクラスは作られてテストも想定通り動くが、Android Studio上ではクラスが参照できずにエラーになる。
https://issuetracker.google.com/issues/37121918

関連したgistに回答があった。
gist.github.com

android {
  defaultConfig {
      android.libraryVariants.all {
        def aptOutputDir = new File(buildDir, "generated/source/apt/${it.unitTestVariant.dirName}")
        it.unitTestVariant.addJavaSourceFoldersToModel(aptOutputDir)
      }
  }
}

Annotation Processor のテスト

Annotation Processor のテスト書きたいなと思って書いてた。

[java] モダンな Annocation Processor の開発手順まとめ - tokuhirom's blog

package com.github.hisaichi5518.konohana.processor;

import com.google.common.truth.Truth;
import com.google.testing.compile.JavaFileObjects;
import com.google.testing.compile.JavaSourceSubjectFactory;

import org.junit.Test;

public class KonohanaProcessorTest {
    @Test
    public void test() {
        Truth.assert_().about(JavaSourceSubjectFactory.javaSource())
                .that(JavaFileObjects.forResource("Success.java"))
                .processedWith(new KonohanaProcessor())
                .compilesWithoutError();
    }
}

コンパイルが通ったらテストが通るというのをやろうとしたら通らない!!!なぜ!!javapoetのTypeName.get() できてない!

java.lang.RuntimeException: java.lang.IllegalArgumentException: part '<any>' is keyword

	at com.sun.tools.javac.main.Main.compile(Main.java:553)
	at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
	at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
	at com.google.testing.compile.Compiler.compile(Compiler.java:146)
	at com.google.testing.compile.JavaSourcesSubject$CompilationClause.compilation(JavaSourcesSubject.java:293)
	at com.google.testing.compile.JavaSourcesSubject$CompilationClause.compilesWithoutError(JavaSourcesSubject.java:267)
	at com.github.hisaichi5518.konohana.processor.KonohanaProcessorTest.test(KonohanaProcessorTest.java:15)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.IllegalArgumentException: part '<any>' is keyword
	at com.squareup.javapoet.Util.checkArgument(Util.java:64)
	at com.squareup.javapoet.ClassName.<init>(ClassName.java:49)
	at com.squareup.javapoet.ClassName.<init>(ClassName.java:43)
	at com.squareup.javapoet.ClassName.get(ClassName.java:218)
	at com.squareup.javapoet.TypeName$1.visitDeclared(TypeName.java:268)
	at com.squareup.javapoet.TypeName$1.visitError(TypeName.java:290)
	at com.squareup.javapoet.TypeName$1.visitError(TypeName.java:243)
	at com.sun.tools.javac.code.Type$ErrorType.accept(Type.java:1855)
	at com.squareup.javapoet.TypeName.get(TypeName.java:243)
	at com.squareup.javapoet.TypeName.get(TypeName.java:238)
	at com.github.hisaichi5518.konohana.processor.definition.KeyDefinition.<init>(KeyDefinition.java:35)
	at com.github.hisaichi5518.konohana.processor.definition.StoreDefinition.lambda$keyDefinitionStream$3(StoreDefinition.java:88)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
	at java.util.Iterator.forEachRemaining(Iterator.java:116)
	at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
	at com.github.hisaichi5518.konohana.processor.builder.StoreMethods.build(StoreMethods.java:23)
	at com.github.hisaichi5518.konohana.processor.writer.StoreWriter.buildTypeSpec(StoreWriter.java:41)
	at com.github.hisaichi5518.konohana.processor.writer.StoreWriter.write(StoreWriter.java:73)
	at com.github.hisaichi5518.konohana.processor.writer.StoreWriter.write(StoreWriter.java:30)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
	at java.util.Iterator.forEachRemaining(Iterator.java:116)
	at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
	at com.github.hisaichi5518.konohana.processor.KonohanaProcessor.process(KonohanaProcessor.java:31)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
	at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856)
	at com.sun.tools.javac.main.Main.compile(Main.java:523)
	... 33 more

コンパイル対象のファイルを見てたら、java.util.Listがimportされてなかった…マジか………。追加したら無事なおった。


追記:
gfxさんにテストの内容について教えてもらった
正常系はlibrary側でやるのがよさそう