パルカワ2

PerlとRubyとイチャラブ

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側でやるのがよさそう


Annotation processorを利用したAndroidライブラリをつくる

作る度に毎度似たようなことをしていてめんどくさい。しかも忘れていたりするのでテンプレート化したいんですが、Android Studioを更新するときにはテンプレートを消さないといけないとかあるらしくてそれはそれでだるいので、とりあえず将来の自分のためにメモとして残しておく。

ここでは、Konohanaという名前のライブラリを作ろうとしています。 名前の由来は、此花区です。かわいい。

全部のログはここにあります。

github.com

New Project

まずは、サンプルアプリを作ります。

File -> New Project を選択

  • Application NameにKonohanaExampleをつける
  • package nameを com.github.hisaichi5518.konohana.exampleに変更する
  • Project Location を Konohana にする

そのあとは自分がmin sdkなどを指定してFinishを押す。 ビルド出来るようになるので、とりあえずビルドして安心感を得る。

f:id:hisaichi5518:20170919172225p:plain

Git

プロジェクト生成が終われば、Gitをアレしてソレする。

  • git init
  • curl https://www.gitignore.io/api/vim%2Candroid%2Candroidstudio > .gitignore
  • git add .
  • git commit -m "first commit"

gitignoreを自分で頑張るのは不毛なので gitignore.io に任せる。僕が利用するVim, Android, Android Studio を指定してある。 Githubリポジトリ作るとかは良きタイミングで。

app -> example

  • app を右クリックして、Refactor -> Rename を選択
  • example にする
  • git commit

Android Libraryを追加する

File -> New Module -> Android Library を選択

  • library name は Konohana
  • module name を library
  • package nameは com.github.hisaichi5518.konohana
  • min sdkはよしなにする
  • git commit

f:id:hisaichi5518:20170919172257p:plain

annotation library (Java Library)を追加する

Android libraryの中にannotationを入れてもいいような気がしますが、後述するProcessor library(Java library)がAndroid libraryに依存するのが許せなかったので分けます。

File -> New Module -> Java Library を選択

  • Library name は annotation
  • package name は com.github.hisaichi5518.konohana.annotation
  • class name は つくる予定のアノテーション名をいれます。 今回の場合は Key
  • git commit

annotations としているライブラリがいくつかありましたが、他は単数形でこれだけ複数形にするのが気になったのでやめました。なにか複数形にするべき理由があるのかもしれない。

f:id:hisaichi5518:20170919172313p:plain

processor library (Java Library) を追加する

File -> New Module -> Java Library を選択

  • Library nameは processor
  • package name は com.github.hisaichi5518.konohana.processor
  • class name は KonohanaProcessor
  • git commit

f:id:hisaichi5518:20170919172324p:plain

Java 8を利用する

processorのみJava8を指定する。

  • processor/build.gradle で JavaVersion.VERSION_1_8 を指定する
  • git commit

https://github.com/hisaichi5518/annotation-processor-example/commit/6ac6d78b59bda4befa5b6dde13e07f2d73dce236

追加したModuleを利用する

以下のように変更し、git commit する。

https://github.com/hisaichi5518/annotation-processor-example/commit/92ce2654b48da75d94f518ea21bd180e87c6a6b4

library/build.gradle

以下を追加する

  • compile project(':annotation')

example/build.gradle

以下を追加する

  • annotationProcessor project(':processor')
  • compile project(':library')

processor/build.gradle

以下を追加する

  • compile project(':annotation')

maven.google.com を利用する

Android Studio 3.0だと不要だと思う。 以下のコミットのように追加する。

https://github.com/hisaichi5518/annotation-processor-example/commit/bcfd9c8a9021e90ffa6b8dd86cfb16be27c10d4e

必要なライブラリを追加と削除

以下のコミットのように追加していく。

https://github.com/hisaichi5518/annotation-processor-example/commit/12d377943af8d35a68be1e52a8210083a0558960

また様々な箇所でバージョンを指定する必要があり、揃える必要があるライブラリ(例えばサポートライブラリ)は、バージョンを変数にいれる。

https://github.com/hisaichi5518/annotation-processor-example/commit/5b0ae810915aa3f4a36f803b0dfc498f5dd48b0d

ライブラリは、最新版を利用したほうがいいので、以下のURLを確認

最後に library/build.gradleにある appcompat-v7 は不要であることもあるので、不要なら消す。

Processorの準備

KonohanaProcessor を変更していく。

https://github.com/hisaichi5518/annotation-processor-example/commit/b59b76ce3989396ab71b29139cf08fcab6e5adc5

Annotationをつくる

自動生成されるのはclassなのでAnnotationにする。 あとexampleで利用するコード書いておく。

@Target(ElementType.FIELD)
public @interface Key {
}
public class MainActivity extends AppCompatActivity {

    @Key
    String name;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Rebuild Projectを実行

Build -> Rebuild Project

おわり

なんかもっとスマートなやり方があったら知りたい。 github.com