diff --git a/.travis.yml b/.travis.yml index 166d0f5..257b8c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,22 +17,13 @@ android: - tools # The BuildTools version used by your project - - build-tools-23.0.2 + - build-tools-25.0.1 # The SDK version used to compile your project - - android-23 + - android-25 # Additional components - #- extra-google-google_play_services - #- extra-google-m2repository - extra-android-m2repository - - extra-android-support - #- addon-google_apis-google-19 - - # Specify at least one system image, - # if you need to run emulator(s) during your tests - #- sys-img-armeabi-v7a-android-19 - #- sys-img-x86-android-17 install: - ./gradlew install diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d880e6..deb36ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Change Log ========== +Version 2.6.2 *(2017-03-11)* +---------------------------- + + * Fix: Processing of `@Inject` annotations for `mvp` module + Version 2.6.1 *(2017-01-28)* ---------------------------- diff --git a/build.gradle b/build.gradle index b31bd1c..0cc55ff 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,13 @@ allprojects { mavenLocal() } + // this is needed for running plugin tests + configurations.all { + resolutionStrategy { + force 'org.codehaus.groovy:groovy-all:2.4.4' + } + } + if (!['Blade', 'sample', 'module'].contains(project.name)) { println " --> $project.name " diff --git a/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/BaseSpecification.groovy b/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/BaseSpecification.groovy index a3c330a..8032a88 100644 --- a/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/BaseSpecification.groovy +++ b/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/BaseSpecification.groovy @@ -8,6 +8,7 @@ import spock.lang.Specification import eu.f3rog.blade.compiler.BladeProcessor import javax.tools.JavaFileObject +import javax.tools.StandardLocation public abstract class BaseSpecification extends Specification { @@ -39,4 +40,20 @@ public abstract class BaseSpecification .processedWith(new BladeProcessor(processorModules)) } } + + protected boolean compilesWithoutErrorAndDoesntGenerate(String pkg, + String className, + BladeProcessor.Module module, + JavaFileObject... inputFiles) { + try { + assertFiles(inputFiles) + .with(module) + .compilesWithoutError() + .and() + .generatesFileNamed(StandardLocation.CLASS_OUTPUT, pkg, className + '.class') + return false + } catch (AssertionError e) { + return e.getMessage().contains("Did not find a generated file corresponding to ${className}.class in package ${pkg}") + } + } } \ No newline at end of file diff --git a/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/arg/ArgHelperSpecification.groovy b/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/arg/ArgHelperSpecification.groovy index 845f63d..2443b40 100644 --- a/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/arg/ArgHelperSpecification.groovy +++ b/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/arg/ArgHelperSpecification.groovy @@ -83,15 +83,8 @@ public final class ArgHelperSpecification ) expect: - try { - assertFiles(input) - .with(BladeProcessor.Module.ARG) - .compilesWithoutError() - .and() - .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "com.example", "MyFragment_Helper.class") - } catch (AssertionError e) { - assert e.getMessage().contains("Did not find a generated file corresponding to MyFragment_Helper.class in package com.example") - } + compilesWithoutErrorAndDoesntGenerate("com.example", "MyFragment_Helper", + BladeProcessor.Module.ARG, input) } def "generate _Helper for a Fragment with 2 @Arg"() { diff --git a/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/mvp/MvpHelperSpecification.groovy b/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/mvp/MvpHelperSpecification.groovy new file mode 100644 index 0000000..8e8be06 --- /dev/null +++ b/core-compiler/src/test/groovy/eu/f3rog/blade/compiler/mvp/MvpHelperSpecification.groovy @@ -0,0 +1,951 @@ +package eu.f3rog.blade.compiler.mvp + +import android.app.Activity +import android.content.Context +import android.support.v4.app.Fragment +import android.view.View +import blade.Blade +import blade.mvp.BasePresenter +import blade.mvp.IPresenter +import blade.mvp.IView +import eu.f3rog.blade.compiler.BaseSpecification +import eu.f3rog.blade.compiler.BladeProcessor +import eu.f3rog.blade.compiler.util.JavaFile +import eu.f3rog.blade.mvp.WeavedMvpActivity +import eu.f3rog.blade.mvp.WeavedMvpFragment +import eu.f3rog.blade.mvp.WeavedMvpView + +import javax.inject.Inject +import javax.tools.JavaFileObject + +public final class MvpHelperSpecification + extends BaseSpecification { + + def "ignore field with @Inject that is not a Presenter"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T { + + @#I Object field; + } + """, + [ + I: Inject.class + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyClass_Helper", + BladeProcessor.Module.MVP, input) + } + + def "ignore Activity (without @Blade) field with @Inject that is not a Presenter"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyActivity", + """ + public class #T extends Activity { + + @#I Object field; + } + """, + [ + I: Inject.class, + _: [Activity.class] + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyActivity_Helper", + BladeProcessor.Module.MVP, input) + } + + def "ignore Fragment (without @Blade) field with @Inject that is not a Presenter"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyActivity", + """ + public class #T extends Fragment { + + @#I Object field; + } + """, + [ + I: Inject.class, + _: [Fragment.class] + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyActivity_Helper", + BladeProcessor.Module.MVP, input) + } + + def "ignore View (without @Blade) field with @Inject that is not a Presenter"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T extends View { + + @#I Object field; + + #T(Context c) { + super(c); + } + } + """, + [ + I: Inject.class, + _: [Context.class, View.class] + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyClass_Helper", + BladeProcessor.Module.MVP, input) + } + + def "ignore constructor with @Inject"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T { + + @#I #T() {} + } + """, + [ + I: Inject.class + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyClass_Helper", + BladeProcessor.Module.MVP, input) + } + + def "fail if unsupported class type tries to inject a Presenter"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T { + + @#I #P presenter; + } + """, + [ + I: Inject.class, + P: IPresenter.class + ] + ) + + expect: + assertFiles(input) + .with(BladeProcessor.Module.MVP) + .failsToCompile() + .withErrorContaining(MvpErrorMsg.Invalid_class_with_injected_Presenter) + } + + def "fail if Activity (without @Blade) tries to inject a Presenter without implementing IView"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T extends Activity { + + @#I #P presenter; + } + """, + [ + I: Inject.class, + P: IPresenter.class, + _: [Activity.class] + ] + ) + + expect: + assertFiles(input) + .with(BladeProcessor.Module.MVP) + .failsToCompile() + .withErrorContaining(MvpErrorMsg.Invalid_class_with_injected_Presenter) + } + + def "fail if Activity (with @Blade) tries to inject a Presenter without implementing IView"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + @#B + public class #T extends Activity { + + @#I #P presenter; + } + """, + [ + B: Blade.class, + I: Inject.class, + P: IPresenter.class, + _: [Activity.class] + ] + ) + + expect: + assertFiles(input) + .with(BladeProcessor.Module.MVP) + .failsToCompile() + .withErrorContaining(String.format(MvpErrorMsg.Invalid_view_class, 'V', 'presenter')) + } + + def "fail if Fragment tries to inject a Presenter without implementing IView"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T extends Fragment { + + @#I #P presenter; + } + """, + [ + I: Inject.class, + P: IPresenter.class, + _: [Fragment.class] + ] + ) + + expect: + assertFiles(input) + .with(BladeProcessor.Module.MVP) + .failsToCompile() + .withErrorContaining(MvpErrorMsg.Invalid_class_with_injected_Presenter) + } + + def "fail if View tries to inject a Presenter without implementing IView"() { + given: + final JavaFileObject input = JavaFile.newFile("com.example", "MyClass", + """ + public class #T extends View { + + @#I #P presenter; + + #T(Context context) { + super(context); + } + } + """, + [ + I: Inject.class, + P: IPresenter.class, + _: [Context.class, View.class] + ] + ) + + expect: + assertFiles(input) + .with(BladeProcessor.Module.MVP) + .failsToCompile() + .withErrorContaining(MvpErrorMsg.Invalid_class_with_injected_Presenter) + } + + def "fail if Activity implements incorrect IView"() { + given: + final JavaFileObject viewInterface1 = JavaFile.newFile("com.example", "IMyView1", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class, + ] + ) + final JavaFileObject viewInterface2 = JavaFile.newFile("com.example", "IMyView2", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class, + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#MV2> { + } + """, + [ + BP : BasePresenter.class, + MV2: viewInterface2, + ] + ) + final JavaFileObject activity = JavaFile.newFile("com.example", "MyActivity", + """ + public class #T extends Activity implements #MV1 { + + @#I #P mPresenter; + } + """, + [ + I : Inject.class, + MV1: viewInterface1, + P : presenter, + _ : [Activity.class] + ] + ) + + expect: + assertFiles(viewInterface1, viewInterface2, presenter, activity) + .with(BladeProcessor.Module.MVP) + .failsToCompile() + .withErrorContaining(String.format(MvpErrorMsg.Invalid_view_class, "com.example.IMyView2", "mPresenter")); + } + + def "ignore if Activity has no @Inject presenter"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class, + ] + ) + final JavaFileObject activity = JavaFile.newFile("com.example", "MyActivity", + """ + public class #T extends Activity implements #MV { + } + """, + [ + MV: viewInterface, + _ : [Activity.class] + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyActivity_Helper", + BladeProcessor.Module.MVP, viewInterface, activity) + } + + def "generate _Helper for Activity with @Blade only"() { + given: + final JavaFileObject activity = JavaFile.newFile("com.example", "MyActivity", + """ + @#B + public class #T extends Activity { + } + """, + [ + B: Blade.class, + _: [Activity.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyActivity_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpActivity.class + ] + ) + + expect: + assertFiles(activity) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "generate _Helper for Activity with 1 Presenter"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject activity = JavaFile.newFile("com.example", "MyActivity", + """ + public class #T extends Activity implements #V { + + @#I #P mPresenter; + } + """, + [ + I: Inject.class, + V: viewInterface, + P: presenter, + _: [Activity.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyActivity_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpActivity.class + ] + ) + + expect: + assertFiles(viewInterface, presenter, activity) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "generate _Helper for Activity with 2 Presenters"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter1 = JavaFile.newFile("com.example", "MyPresenter1", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject presenter2 = JavaFile.newFile("com.example", "MyPresenter2", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject activity = JavaFile.newFile("com.example", "MyActivity", + """ + public class #T extends Activity implements #V { + + @#I #P1 mPresenter1; + @#I #P2 mPresenter2; + } + """, + [ + I : Inject.class, + V : viewInterface, + P1: presenter1, + P2: presenter2, + _ : [Activity.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyActivity_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpActivity.class + ] + ) + + expect: + assertFiles(viewInterface, presenter1, presenter2, activity) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "ignore Fragment without Presenter"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject fragment = JavaFile.newFile("com.example", "MyFragment", + """ + public class #T extends Fragment implements #V { + } + """, + [ + V: viewInterface, + _: [Fragment.class] + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyFragment_Helper", + BladeProcessor.Module.MVP, viewInterface, fragment) + } + + def "generate _Helper for Fragment with 1 Presenter"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject fragment = JavaFile.newFile("com.example", "MyFragment", + """ + public class #T extends Fragment implements #V { + + @#I #P mPresenter; + } + """, + [ + I: Inject.class, + V: viewInterface, + P: presenter, + _: [Fragment.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyFragment_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpFragment.class + ] + ) + + expect: + assertFiles(viewInterface, presenter, fragment) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "generate _Helper for Fragment with 2 Presenters"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter1 = JavaFile.newFile("com.example", "MyPresenter1", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject presenter2 = JavaFile.newFile("com.example", "MyPresenter2", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject fragment = JavaFile.newFile("com.example", "MyFragment", + """ + public class #T extends Fragment implements #V { + + @#I #P1 mPresenter1; + @#I #P2 mPresenter2; + } + """, + [ + I : Inject.class, + V : viewInterface, + P1: presenter1, + P2: presenter2, + _ : [Fragment.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyFragment_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpFragment.class + ] + ) + + expect: + assertFiles(viewInterface, presenter1, presenter2, fragment) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "ignore View without Presenter"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject view = JavaFile.newFile("com.example", "MyView", + """ + public class #T extends View implements #V { + + #T(Context c) { + super(c); + } + } + """, + [ + V: viewInterface, + _: [Context.class, View.class] + ] + ) + + expect: + compilesWithoutErrorAndDoesntGenerate("com.example", "MyView_Helper", + BladeProcessor.Module.MVP, viewInterface, view) + } + + def "generate _Helper for View with 1 Presenter"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject view = JavaFile.newFile("com.example", "MyView", + """ + public class #T extends View implements #V { + + @#I #P mPresenter; + + #T(Context c) { + super(c); + } + } + """, + [ + I: Inject.class, + V: viewInterface, + P: presenter, + _: [Context.class, View.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyView_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpView.class + ] + ) + + expect: + assertFiles(viewInterface, presenter, view) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "generate _Helper for View with 2 Presenters"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter1 = JavaFile.newFile("com.example", "MyPresenter1", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject presenter2 = JavaFile.newFile("com.example", "MyPresenter2", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject view = JavaFile.newFile("com.example", "MyView", + """ + public class #T extends View implements #V { + + @#I #P1 mPresenter1; + @#I #P2 mPresenter2; + + #T(Context c) { + super(c); + } + } + """, + [ + I : Inject.class, + V : viewInterface, + P1: presenter1, + P2: presenter2, + _ : [Context.class, View.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "MyView_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpView.class + ] + ) + + expect: + assertFiles(viewInterface, presenter1, presenter2, view) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected) + } + + def "generate _Helper for inner Activity with 1 Presenters"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject activity = JavaFile.newFile("com.example", "Wrapper", + """ + public class #T { + public static class MyActivity extends Activity implements #V { + @#I #P mPresenter; + } + } + """, + [ + I: Inject.class, + V: viewInterface, + P: presenter, + _: [Activity.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "Wrapper_MyActivity_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpActivity.class + ] + ) + + expect: + assertFiles(viewInterface, presenter, activity) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected); + } + + def "generate _Helper for inner Fragment with 1 Presenters"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject fragment = JavaFile.newFile("com.example", "Wrapper", + """ + public class #T { + public static class MyFragment extends Fragment implements #V { + @#I #P mPresenter; + } + } + """, + [ + I: Inject.class, + V: viewInterface, + P: presenter, + _: [Fragment.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "Wrapper_MyFragment_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpFragment.class + ] + ) + + expect: + assertFiles(viewInterface, presenter, fragment) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected); + } + + def "generate _Helper for inner View with 1 Presenters"() { + given: + final JavaFileObject viewInterface = JavaFile.newFile("com.example", "IMyView", + """ + public interface #T extends #V { + } + """, + [ + V: IView.class + ] + ) + final JavaFileObject presenter = JavaFile.newFile("com.example", "MyPresenter", + """ + public class #T extends #BP<#V> { + } + """, + [ + BP: BasePresenter, + V : viewInterface, + ] + ) + final JavaFileObject activity = JavaFile.newFile("com.example", "Wrapper", + """ + public class #T { + public static class MyView extends View implements #V { + @#I #P mPresenter; + + MyView(Context c) { + super(c); + } + } + } + """, + [ + I: Inject.class, + V: viewInterface, + P: presenter, + _: [Context.class, View.class] + ] + ) + + final JavaFileObject expected = JavaFile.newGeneratedFile("com.example", "Wrapper_MyView_Helper", + """ + abstract class #T implements #M { + } + """, + [ + M: WeavedMvpView.class + ] + ) + + expect: + assertFiles(viewInterface, presenter, activity) + .with(BladeProcessor.Module.MVP) + .compilesWithoutError() + .and() + .generatesSources(expected); + } +} \ No newline at end of file diff --git a/core-compiler/src/test/java/eu/f3rog/blade/compiler/mvp/InnerClassTest.java b/core-compiler/src/test/java/eu/f3rog/blade/compiler/mvp/InnerClassTest.java deleted file mode 100644 index d88ca8d..0000000 --- a/core-compiler/src/test/java/eu/f3rog/blade/compiler/mvp/InnerClassTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package eu.f3rog.blade.compiler.mvp; - -import android.app.Activity; -import android.content.Context; -import android.view.View; - -import org.junit.Test; - -import javax.inject.Inject; -import javax.tools.JavaFileObject; - -import blade.mvp.IPresenter; -import blade.mvp.IView; -import blade.mvp.PresenterManager; -import eu.f3rog.blade.compiler.BaseTest; -import eu.f3rog.blade.compiler.BladeProcessor; -import eu.f3rog.blade.core.Weave; -import eu.f3rog.blade.mvp.WeavedMvpActivity; - -import static eu.f3rog.blade.compiler.util.File.file; -import static eu.f3rog.blade.compiler.util.File.generatedFile; - -/** - * Class {@link InnerClassTest} - * - * @author FrantisekGazo - */ -public final class InnerClassTest - extends BaseTest { - - @Test - public void inner() { - JavaFileObject presenter = file("com.example", "MyPresenter") - .imports( - IPresenter.class, "P", - "com.example.Wrapper", "V" - ) - .body( - "public class $T implements $P<$V.MyView> {", - "", - PresenterTest.getPresenterImplementation("$V.MyView"), - "}" - ); - JavaFileObject view = file("com.example", "Wrapper") - .imports( - presenter, "MP", - Activity.class, - Inject.class, "I", - IView.class, "V" - ) - .body( - "public class $T {", - "", - " public static class MyView extends Activity implements $V {", - "", - " @$I $MP mPresenter;", - "", - " }", - "}" - ); - - JavaFileObject expected = generatedFile("com.example", "Wrapper_MyView_Helper") - .imports( - WeavedMvpActivity.class, "I" - ) - .body( - "abstract class $T implements $I {", - "}" - ); - - assertFiles(presenter, view) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } -} diff --git a/core-compiler/src/test/java/eu/f3rog/blade/compiler/mvp/PresenterTest.java b/core-compiler/src/test/java/eu/f3rog/blade/compiler/mvp/PresenterTest.java deleted file mode 100644 index 2768c17..0000000 --- a/core-compiler/src/test/java/eu/f3rog/blade/compiler/mvp/PresenterTest.java +++ /dev/null @@ -1,602 +0,0 @@ -package eu.f3rog.blade.compiler.mvp; - -import android.app.Activity; -import android.app.Fragment; -import android.content.Context; -import android.view.View; - -import org.junit.Assert; -import org.junit.Test; - -import javax.inject.Inject; -import javax.tools.JavaFileObject; - -import blade.Blade; -import blade.mvp.BasePresenter; -import blade.mvp.IPresenter; -import blade.mvp.IView; -import eu.f3rog.blade.compiler.BaseTest; -import eu.f3rog.blade.compiler.BladeProcessor; -import eu.f3rog.blade.mvp.WeavedMvpActivity; -import eu.f3rog.blade.mvp.WeavedMvpFragment; - -import static eu.f3rog.blade.compiler.util.File.file; -import static eu.f3rog.blade.compiler.util.File.generatedFile; - -/** - * Class {@link PresenterTest} - * - * @author FrantisekGazo - */ -public final class PresenterTest - extends BaseTest { - - static String getPresenterImplementation(String viewType) { - return String.format(PRESENTER_METHODS, viewType, viewType); - } - - private static final String PRESENTER_METHODS = - " public void onBind(%s view) {} " + - " public %s getView() {return null;} " + - " public void onUnbind() {} " + - " public void onCreate(Object state) {} " + - " public void onDestroy() {} " + - " public void onSaveState(Object state) {} "; - - @Test - public void ignoredInjections() { - JavaFileObject input = file("com.example", "MyClass") - .imports( - Inject.class, "I" - ) - .body( - "public class $T {", - "", - " @$I Object o;", - "", - "}" - ); - - assertFiles(input) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError(); - - assertFileNotGenerated("com.example", "MyClass_Helper", input); - - input = file("com.example", "MyClass") - .imports( - Inject.class, "I", - View.class, - Context.class - ) - .body( - "public class $T extends View {", - "", - " @$I Object o;", - "", - " $T(Context c) {super(c);}", - "", - "}" - ); - - assertFiles(input) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError(); - - assertFileNotGenerated("com.example", "MyClass_Helper", input); - } - - @Test - public void invalidClassWithInjectedPresenter() { - JavaFileObject input = file("com.example", "MyClass") - .imports( - Inject.class, "I", - IPresenter.class, "P" - ) - .body( - "public class $T {", - "", - " @$I $P o;", - "", - "}" - ); - - assertFiles(input) - .with(BladeProcessor.Module.MVP) - .failsToCompile() - .withErrorContaining(MvpErrorMsg.Invalid_class_with_injected_Presenter); - - input = file("com.example", "MyClass") - .imports( - Inject.class, "I", - IPresenter.class, "P", - View.class, - Context.class - ) - .body( - "public class $T extends View {", - "", - " @$I $P o;", - "", - " $T(Context c) {super(c);}", - "", - "}" - ); - - assertFiles(input) - .with(BladeProcessor.Module.MVP) - .failsToCompile() - .withErrorContaining(MvpErrorMsg.Invalid_class_with_injected_Presenter); - } - - @Test - public void incorrectTypes() { - JavaFileObject viewInterface1 = file("com.example", "IMyView1") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject viewInterface2 = file("com.example", "IMyView2") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject presenter = file("com.example", "MyPresenter") - .imports( - BasePresenter.class, "P", - viewInterface2, "MV2" - ) - .body( - "public class $T extends $P<$MV2> {", - "", - "}" - ); - JavaFileObject activity = file("com.example", "MyActivity") - .imports( - Activity.class, - viewInterface1, "MV1", - presenter, "P", - Inject.class, "I" - ) - .body( - "public class $T extends Activity implements $MV1 {", - "", - " @$I $P mPresenter;", - "", - "}" - ); - - assertFiles(viewInterface1, viewInterface2, presenter, activity) - .with(BladeProcessor.Module.MVP) - .failsToCompile() - .withErrorContaining(String.format(MvpErrorMsg.Invalid_view_class, "com.example.IMyView2", "mPresenter")); - } - - @Test - public void activityWithoutPresenter() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject activity = file("com.example", "MyActivity") - .imports( - Activity.class, - viewInterface, "MV", - Inject.class, "I" - ) - .body( - "public class $T extends Activity implements $MV {", - "}" - ); - - assertFiles(viewInterface, activity) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError(); - - assertFileNotGenerated("com.example", "MyActivity_Helper", viewInterface, activity); - } - - @Test - public void activityWithoutPresenterWithBladeImplementingIView() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject activity = file("com.example", "MyActivity") - .imports( - Activity.class, - viewInterface, "MV", - Blade.class, "B", - Inject.class, "I" - ) - .body( - "@$B", - "public class $T extends Activity implements $MV {", - "}" - ); - - - JavaFileObject expected = generatedFile("com.example", "MyActivity_Helper") - .imports( - WeavedMvpActivity.class, "M" - ) - .body( - "abstract class $T implements $M {", - "}" - ); - - assertFiles(viewInterface, activity) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } - - @Test - public void activityWithoutPresenterWithBlade() { - JavaFileObject activity = file("com.example", "MyActivity") - .imports( - Activity.class, - Blade.class, "B" - ) - .body( - "@$B", - "public class $T extends Activity {", - "}" - ); - - - JavaFileObject expected = generatedFile("com.example", "MyActivity_Helper") - .imports( - WeavedMvpActivity.class, "M" - ) - .body( - "abstract class $T implements $M {", - "}" - ); - - assertFiles(activity) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } - - @Test - public void activityWith1Presenter() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject presenter = file("com.example", "MyPresenter") - .imports( - IPresenter.class, "P", - viewInterface, "MV" - ) - .body( - "public class $T implements $P<$MV> {", - "", - getPresenterImplementation("$MV"), - "", - "}" - ); - JavaFileObject activity = file("com.example", "MyActivity") - .imports( - Activity.class, - viewInterface, "MV", - Inject.class, "I", - presenter, "MP" - ) - .body( - "public class $T extends Activity implements $MV {", - "", - " @$I $MP mPresenter;", - "", - "}" - ); - - JavaFileObject expected = generatedFile("com.example", "MyActivity_Helper") - .imports( - WeavedMvpActivity.class, "M" - ) - .body( - "abstract class $T implements $M {", - "}" - ); - - assertFiles(viewInterface, presenter, activity) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } - - @Test - public void activityWith2Presenters() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject presenter1 = file("com.example", "MyPresenter1") - .imports( - IPresenter.class, "P", - viewInterface, "MV" - ) - .body( - "public class $T implements $P<$MV> {", - "", - getPresenterImplementation("$MV"), - "", - "}" - ); - JavaFileObject presenter2 = file("com.example", "MyPresenter2") - .imports( - IPresenter.class, "P", - viewInterface, "MV" - ) - .body( - "public class $T implements $P<$MV> {", - "", - getPresenterImplementation("$MV"), - "", - "}" - ); - JavaFileObject activity = file("com.example", "MyActivity") - .imports( - Activity.class, - viewInterface, "MV", - Inject.class, "I", - presenter1, "MP1", - presenter2, "MP2" - ) - .body( - "public class $T extends Activity implements $MV {", - "", - " @$I $MP1 mPresenter1;", - " @$I $MP2 mPresenter2;", - "", - "}" - ); - - JavaFileObject expected = generatedFile("com.example", "MyActivity_Helper") - .imports( - WeavedMvpActivity.class, "M" - ) - .body( - "abstract class $T implements $M {", - "}" - ); - - assertFiles(viewInterface, presenter1, presenter2, activity) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } - - @Test - public void fragmentWithoutPresenter() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject fragment = file("com.example", "MyFragment") - .imports( - Fragment.class, - viewInterface, "MV", - Inject.class, "I" - ) - .body( - "public class $T extends Fragment implements $MV {", - "}" - ); - - assertFiles(viewInterface, fragment) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError(); - - assertFileNotGenerated("com.example", "MyFragment_Helper", viewInterface, fragment); - } - - @Test - public void fragmentWithoutPresenterWithBlade() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject fragment = file("com.example", "MyFragment") - .imports( - Fragment.class, - viewInterface, "MV", - Blade.class, "B", - Inject.class, "I" - ) - .body( - "@$B", - "public class $T extends Fragment implements $MV {", - "}" - ); - - assertFiles(viewInterface, fragment) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError(); - - assertFileNotGenerated("com.example", "MyFragment_Helper", viewInterface, fragment); - } - - @Test - public void fragmentWith1Presenter() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject presenter = file("com.example", "MyPresenter") - .imports( - IPresenter.class, "P", - viewInterface, "MV" - ) - .body( - "public class $T implements $P<$MV> {", - "", - getPresenterImplementation("$MV"), - "", - "}" - ); - JavaFileObject fragment = file("com.example", "MyFragment") - .imports( - Fragment.class, - viewInterface, "MV", - Inject.class, "I", - presenter, "MP" - ) - .body( - "public class $T extends Fragment implements $MV {", - "", - " @$I $MP mPresenter;", - "", - "}" - ); - - JavaFileObject expected = generatedFile("com.example", "MyFragment_Helper") - .imports( - WeavedMvpFragment.class, "M" - ) - .body( - "abstract class $T implements $M {", - "}" - ); - - assertFiles(viewInterface, presenter, fragment) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } - - @Test - public void fragmentWith2Presenters() { - JavaFileObject viewInterface = file("com.example", "IMyView") - .imports( - IView.class, "V" - ) - .body( - "public interface $T extends $V {", - "}" - ); - JavaFileObject presenter1 = file("com.example", "MyPresenter1") - .imports( - IPresenter.class, "P", - viewInterface, "MV" - ) - .body( - "public class $T implements $P<$MV> {", - "", - getPresenterImplementation("$MV"), - "", - "}" - ); - JavaFileObject presenter2 = file("com.example", "MyPresenter2") - .imports( - IPresenter.class, "P", - viewInterface, "MV" - ) - .body( - "public class $T implements $P<$MV> {", - "", - getPresenterImplementation("$MV"), - "", - "}" - ); - JavaFileObject fragment = file("com.example", "MyFragment") - .imports( - Fragment.class, - viewInterface, "MV", - Inject.class, "I", - presenter1, "MP1", - presenter2, "MP2" - ) - .body( - "public class $T extends Fragment implements $MV {", - "", - " @$I $MP1 mPresenter1;", - " @$I $MP2 mPresenter2;", - "", - "}" - ); - - JavaFileObject expected = generatedFile("com.example", "MyFragment_Helper") - .imports( - WeavedMvpFragment.class, "M" - ) - .body( - "abstract class $T implements $M {", - "}" - ); - - assertFiles(viewInterface, presenter1, presenter2, fragment) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - } - - private void assertFileNotGenerated(String pack, String className, JavaFileObject... files) { - try { - JavaFileObject expected = generatedFile(pack, className) - .imports() - .body("class $T {}"); - - // 'expected' file should not be created, because class does not contain any Presenter. - // compile should throw AssertionError. - assertFiles(files) - .with(BladeProcessor.Module.MVP) - .compilesWithoutError() - .and() - .generatesSources(expected); - - Assert.assertTrue(false); - } catch (AssertionError e) { - String message = e.getMessage(); - Assert.assertTrue( - message.contains("An expected source declared one or more top-level types that were not present.") - ); - Assert.assertTrue( - message.contains(String.format("Expected top-level types: <[%s.%s]>", pack, className)) - ); - } - } -} diff --git a/gradle.properties b/gradle.properties index eba3019..f9fbd7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1536M LIB_GROUP_ID = eu.f3rog.blade ARTIFACT_ID = none -LIB_VERSION = 2.6.1 +LIB_VERSION = 2.6.2 LIB_VERSION_DESC = Android Library for Boilerplate Destruction diff --git a/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/MvpProcessorModule.java b/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/MvpProcessorModule.java index 2594daf..6d0df52 100644 --- a/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/MvpProcessorModule.java +++ b/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/MvpProcessorModule.java @@ -5,6 +5,7 @@ import javax.annotation.processing.RoundEnvironment; import javax.inject.Inject; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; @@ -30,7 +31,12 @@ public void process(TypeElement bladeElement) throws ProcessorError { @Override public void process(RoundEnvironment roundEnv) throws ProcessorError { Set elements = roundEnv.getElementsAnnotatedWith(Inject.class); - for (Element e : elements) { + for (final Element e : elements) { + // process only field injections + if (e.getKind() != ElementKind.FIELD) { + continue; + } + TypeElement typeElement = (TypeElement) e.getEnclosingElement(); PresenterHelperModule module = ClassManager.getInstance() diff --git a/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/PresenterHelperModule.java b/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/PresenterHelperModule.java index 11af221..dc6ea93 100644 --- a/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/PresenterHelperModule.java +++ b/module/mvp-compiler/src/main/java/eu/f3rog/blade/compiler/mvp/PresenterHelperModule.java @@ -12,6 +12,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import blade.Blade; import blade.mvp.IPresenter; import blade.mvp.IView; import eu.f3rog.blade.compiler.builder.helper.BaseHelperModule; @@ -54,13 +55,18 @@ public void checkClass(TypeElement e) throws ProcessorError { } else if (isSubClassOf(e, View.class)) { viewType = ViewType.VIEW; } - } else if (isActivitySubClass(e)) { + } else if (isActivitySubClass(e) && hasBladeAnnotation(e)) { viewType = ViewType.ACTIVITY; } mViewType = viewType; } + private boolean hasBladeAnnotation(TypeElement typeElement) { + final Blade annotation = typeElement.getAnnotation(Blade.class); + return annotation != null; + } + @Override public void add(VariableElement e) throws ProcessorError { TypeName presenterType = getSuperType(getTypeElement(ClassName.get(e.asType())), IPresenter.class); diff --git a/plugin/src/main/groovy/eu/f3rog/blade/plugin/BladePlugin.groovy b/plugin/src/main/groovy/eu/f3rog/blade/plugin/BladePlugin.groovy index daa777e..2523240 100644 --- a/plugin/src/main/groovy/eu/f3rog/blade/plugin/BladePlugin.groovy +++ b/plugin/src/main/groovy/eu/f3rog/blade/plugin/BladePlugin.groovy @@ -32,7 +32,7 @@ public final class BladePlugin public static String ERROR_CONFIG_FILE_IS_MISSING = "Blade configuration file is missing! (more info here: https://github.com/FrantisekGazo/Blade/wiki#1-create-configuration-file)" public static String LIB_GROUP_ID = "eu.f3rog.blade" - public static String LIB_VERSION = "2.6.1" + public static String LIB_VERSION = "2.6.2" public static String LIB_CONFIG_FILE_NAME = "blade" public static String[] LIB_MODULES = ["arg", "extra", "mvp", "parcel", "state"] diff --git a/plugin/src/test/groovy/eu/f3rog/ptu/GradleTempFileBuilder.groovy b/plugin/src/test/groovy/eu/f3rog/ptu/GradleTempFileBuilder.groovy index 655ab07..84f09e5 100644 --- a/plugin/src/test/groovy/eu/f3rog/ptu/GradleTempFileBuilder.groovy +++ b/plugin/src/test/groovy/eu/f3rog/ptu/GradleTempFileBuilder.groovy @@ -17,13 +17,13 @@ package eu.f3rog.ptu final String androidConfig if (hasAndroidPlugin()) { androidConfig = """android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 25 + buildToolsVersion "25.0.1" defaultConfig { applicationId "com.example" minSdkVersion 14 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 1 versionName "1.0" } @@ -33,6 +33,9 @@ package eu.f3rog.ptu proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + lintOptions { + abortOnError false + } } """ } else { diff --git a/sample/build.gradle b/sample/build.gradle index f1cb0fa..4458e86 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -9,7 +9,7 @@ buildscript { } classpath 'com.android.tools.build:gradle:2.2.2' - classpath 'eu.f3rog.blade:plugin:2.6.0' + classpath 'eu.f3rog.blade:plugin:2.6.2' } } @@ -17,13 +17,13 @@ apply plugin: 'com.android.application' apply plugin: 'blade' android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 25 + buildToolsVersion "25.0.1" defaultConfig { applicationId "eu.f3rog.blade.sample" minSdkVersion 14 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 1 versionName "1.0" } @@ -56,14 +56,26 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' - - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.android.support:appcompat-v7:25.0.1' + compile 'com.android.support:design:25.0.1' + compile 'com.android.support:recyclerview-v7:25.0.1' - compile 'io.reactivex:rxandroid:1.1.0' - compile 'io.reactivex:rxjava:1.1.0' + /* Dagger2 - needed for MVP module */ + compile 'com.google.dagger:dagger:2.8' + annotationProcessor 'com.google.dagger:dagger-compiler:2.8' + /* ButterKnife */ + compile 'com.jakewharton:butterknife:8.4.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' + /* Rx */ + compile 'io.reactivex:rxandroid:1.2.1' + compile 'io.reactivex:rxjava:1.2.5' + /* REST API */ + compile 'com.google.code.gson:gson:2.6.2' + compile 'com.squareup.retrofit2:retrofit:2.1.0' + compile 'com.squareup.retrofit2:converter-gson:2.1.0' + compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' + compile 'com.squareup.picasso:picasso:2.5.2' + compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' - compile 'com.google.dagger:dagger:2.0.2' - annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2' -} \ No newline at end of file + testCompile 'junit:junit:4.12' +} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 1671394..9edc746 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,9 +1,12 @@ - + + + - + + + + diff --git a/sample/src/main/java/eu/f3rog/blade/sample/MainActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/MainActivity.java index f91b5da..c7676ab 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/MainActivity.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/MainActivity.java @@ -26,7 +26,7 @@ public void gotoState() { @OnClick(R.id.btn_go_mvp) public void gotoMvp() { - I.startTestMvpActivity(this); + I.startMvpMainActivity(this); } @OnClick(R.id.btn_go_parcel) diff --git a/sample/src/main/java/eu/f3rog/blade/sample/arg/DataFragment.java b/sample/src/main/java/eu/f3rog/blade/sample/arg/DataFragment.java index 60103b1..0bfa6e2 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/arg/DataFragment.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/arg/DataFragment.java @@ -8,7 +8,7 @@ import android.widget.TextView; import blade.Arg; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import eu.f3rog.blade.sample.R; @@ -23,11 +23,11 @@ public class DataFragment extends BaseFragment { @Arg Data data; - @Bind(R.id.txt_value_int) + @BindView(R.id.txt_value_int) TextView mTxtInteger; - @Bind(R.id.txt_value_string) + @BindView(R.id.txt_value_string) TextView mTxtString; - @Bind(R.id.txt_value_double) + @BindView(R.id.txt_value_double) TextView mTxtDouble; @Nullable diff --git a/sample/src/main/java/eu/f3rog/blade/sample/arg/TestArgActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/arg/TestArgActivity.java index cc1e92f..cfdbe78 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/arg/TestArgActivity.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/arg/TestArgActivity.java @@ -8,7 +8,7 @@ import blade.Blade; import blade.F; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import eu.f3rog.blade.sample.R; @@ -16,9 +16,9 @@ @Blade public class TestArgActivity extends AppCompatActivity { - @Bind(R.id.edt_num) + @BindView(R.id.edt_num) EditText mEditNumber; - @Bind(R.id.edt_text) + @BindView(R.id.edt_text) EditText mEditText; @OnClick(R.id.btn_show_fragment) diff --git a/sample/src/main/java/eu/f3rog/blade/sample/extra/ExtraTextActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/extra/ExtraTextActivity.java index 2fcaeb0..c7735b5 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/extra/ExtraTextActivity.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/extra/ExtraTextActivity.java @@ -5,7 +5,7 @@ import android.widget.TextView; import blade.Extra; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import eu.f3rog.blade.sample.R; @@ -14,7 +14,7 @@ public class ExtraTextActivity extends AppCompatActivity { @Extra String mShowText; - @Bind(R.id.txt) + @BindView(R.id.txt) TextView mTextView; @Override diff --git a/sample/src/main/java/eu/f3rog/blade/sample/extra/TestExtraActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/extra/TestExtraActivity.java index daa2da2..57765fd 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/extra/TestExtraActivity.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/extra/TestExtraActivity.java @@ -6,7 +6,7 @@ import blade.Blade; import blade.I; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import eu.f3rog.blade.sample.R; @@ -14,7 +14,7 @@ @Blade public class TestExtraActivity extends AppCompatActivity { - @Bind(R.id.edt) + @BindView(R.id.edt) EditText mEditText; @OnClick(R.id.btn_show_text) diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/AppComponent.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/AppComponent.java index 68903aa..aa8a65a 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/AppComponent.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/AppComponent.java @@ -1,24 +1,33 @@ package eu.f3rog.blade.sample.mvp.di.component; +import javax.inject.Singleton; + import dagger.Component; -import eu.f3rog.blade.sample.mvp.ui.activity.TestMvpActivity; -import eu.f3rog.blade.sample.mvp.di.module.PresenterModule; -import eu.f3rog.blade.sample.mvp.di.module.RxModule; -import eu.f3rog.blade.sample.mvp.ui.fragment.TestMvpDialogFragment; -import eu.f3rog.blade.sample.mvp.ui.fragment.TestMvpFragment; -import eu.f3rog.blade.sample.mvp.ui.view.DataView; +import eu.f3rog.blade.sample.mvp.di.module.app.AppModule; +import eu.f3rog.blade.sample.mvp.di.module.data.DataModule; +import eu.f3rog.blade.sample.mvp.di.module.presenter.PresenterModule; +import eu.f3rog.blade.sample.mvp.presenter.ActorListPresenter; +import eu.f3rog.blade.sample.mvp.ui.activity.ActorsActivity; +import eu.f3rog.blade.sample.mvp.ui.fragment.ActorDialogFragment; +import eu.f3rog.blade.sample.mvp.ui.fragment.ActorFragment; +import eu.f3rog.blade.sample.mvp.ui.view.ActorCustomView; +@Singleton @Component(modules = { - RxModule.class, + AppModule.class, + DataModule.class, PresenterModule.class }) public interface AppComponent { - void inject(TestMvpActivity activity); + void inject(ActorsActivity activity); + + void inject(ActorFragment fragment); - void inject(TestMvpFragment fragment); + void inject(ActorDialogFragment fragment); - void inject(TestMvpDialogFragment fragment); + void inject(ActorCustomView view); - void inject(DataView view); + // for tests + ActorListPresenter actorListPresenter(); } diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/Component.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/Component.java index dc10555..1210960 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/Component.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/component/Component.java @@ -1,27 +1,35 @@ package eu.f3rog.blade.sample.mvp.di.component; -import eu.f3rog.blade.sample.mvp.di.module.PresenterModule; -import eu.f3rog.blade.sample.mvp.di.module.RxModule; +import android.app.Application; +import android.support.annotation.NonNull; + +import eu.f3rog.blade.sample.mvp.di.module.app.AppModule; +import eu.f3rog.blade.sample.mvp.di.module.data.DataModule; +import eu.f3rog.blade.sample.mvp.di.module.presenter.PresenterModule; /** * Class {@link Component} * * @author FrantisekGazo - * @version 2016-02-26 */ public class Component { private static AppComponent sAppComponent = null; public static AppComponent forApp() { - if (sAppComponent == null) { - sAppComponent = DaggerAppComponent - .builder() - .rxModule(new RxModule()) - .presenterModule(new PresenterModule()) - .build(); - } return sAppComponent; } + public static void initAppComponent(Application app) { + sAppComponent = createAppComponent(new AppModule(app)); + } + + public static AppComponent createAppComponent(@NonNull final AppModule appModule) { + return DaggerAppComponent + .builder() + .appModule(appModule) + .dataModule(new DataModule()) + .presenterModule(new PresenterModule()) + .build(); + } } diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/PresenterModule.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/PresenterModule.java deleted file mode 100644 index 254ceca..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/PresenterModule.java +++ /dev/null @@ -1,17 +0,0 @@ -package eu.f3rog.blade.sample.mvp.di.module; - -import dagger.Module; -import dagger.Provides; -import eu.f3rog.blade.sample.mvp.di.q.SchedulerType; -import eu.f3rog.blade.sample.mvp.presenter.DataPresenter; -import rx.Scheduler; - - -@Module -public class PresenterModule { - - @Provides - public DataPresenter providesDataPresenter(@SchedulerType.Main Scheduler s1, @SchedulerType.Task Scheduler s2) { - return new DataPresenter(s1, s2); - } -} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/app/AppModule.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/app/AppModule.java new file mode 100644 index 0000000..3f502aa --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/app/AppModule.java @@ -0,0 +1,30 @@ +package eu.f3rog.blade.sample.mvp.di.module.app; + +import android.app.Application; +import android.content.Context; +import android.support.annotation.NonNull; + +import dagger.Module; +import dagger.Provides; + + +@Module +public class AppModule { + + @NonNull + private final Application mApp; + + public AppModule(@NonNull final Application app) { + mApp = app; + } + + @Provides + public Application provideApp() { + return mApp; + } + + @Provides + public Context provideAppContext() { + return mApp.getApplicationContext(); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/DataModule.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/DataModule.java new file mode 100644 index 0000000..1ab82d7 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/DataModule.java @@ -0,0 +1,64 @@ +package eu.f3rog.blade.sample.mvp.di.module.data; + +import android.content.Context; +import android.support.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import eu.f3rog.blade.sample.mvp.di.module.data.api.ActorApi; +import eu.f3rog.blade.sample.mvp.service.DataService; +import eu.f3rog.blade.sample.mvp.service.ImageLoader; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + + +@Module +public class DataModule { + + @Provides + @Singleton + public Gson provideGson() { + return new GsonBuilder() + // here you can add custom gson type adapters + .create(); + } + + @Provides + @Singleton + public OkHttpClient provideOkHttpClient() { + return new OkHttpClient.Builder().build(); + } + + @Provides + @Singleton + public ImageLoader provideImageLoader(@NonNull Context appContext, @NonNull OkHttpClient httpClient) { + return new PicassoImageLoader(appContext, httpClient); + } + + @Provides + @Singleton + public ActorApi provideActorApi(@NonNull OkHttpClient httpClient, @NonNull final Gson gson) { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(ActorApi.ENDPOINT) + .client(httpClient) + .addConverterFactory(GsonConverterFactory.create(gson)) + .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .build(); + + return retrofit.create(ActorApi.class); + } + + @Provides + @Singleton + public DataService providesDataService(@NonNull final ActorApi api) { + return new FakeDataService(); +// return new OnlineDataService(api); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/FakeDataService.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/FakeDataService.java new file mode 100644 index 0000000..9fdabfa --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/FakeDataService.java @@ -0,0 +1,148 @@ +package eu.f3rog.blade.sample.mvp.di.module.data; + + +import android.content.res.Resources; +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import eu.f3rog.blade.sample.mvp.model.Actor; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import eu.f3rog.blade.sample.mvp.service.DataService; +import rx.Single; +import rx.SingleEmitter; +import rx.functions.Action1; +import rx.schedulers.Schedulers; + +/* package */ final class FakeDataService implements DataService { + + @NonNull + private final List mActors; + @NonNull + private final static String INFO = "Eos cogitantur sum aliquandiu contingere. Explicui callidus gi profecto actiones addamque collecta si. Mecum signa erant serie novas et ut. Fundamenta mo facultatem ha is diligenter durationis. Aliquam mea aperire scripti dicerem usu erumpam quodque sed. Deceptorem aliquamdiu hac pro praesertim ita. Alios ferre famam agi dat quasi ferri ideis. Quicquam rem diversae concipio sap sessione rom recordor ita. Is ab reddat angeli ex latere realem mendax cetera." + + "\n\n" + + "Aestimare perceptio archetypi fruebatur immittant mem hoc iii sic. Rei ita haberem fecisse uno formari mea probari. Angelos ibi ignorem deesset rum lapidis respexi caeteri nul cau. Locis pauci adsit fecto vim voces miror nia. Negari re sequor illico posita notatu quanto ut. Attributa vox cunctatus explorant delusisse instituti nul praestari. Humanae insuper essendi se ejusque to columen positis." + + "\n\n" + + "Odoratum diversum vos rerumque ope efficiat hos qua realitas locusque. To ii ex efficiat defectus majorque immorari ad realiter infinite. Mendax perire simile mo ne causae ea co. Ii neque major ob habeo. Quies is omnia ei somni. Omne apti ad rogo novi is omni inde." + + "\n\n" + + "Caeteras diversae ha alicujus im at de. Fallit du solius summae ac cumque ea. Is addamque reductis cessarem ac eo et. Eo falleret co ac posuisse insidias. Mei credent gallico usu peccant credere fatigor positis vis. Materia retinet frustra in ac quaenam vi ex. Dari ille agi vim quis. An caelum simile judico magnis moveri ac. Seipsum in quatuor visione deumque ob ex attendo." + + "\n\n" + + "Perfectum ii infirmari partiales percipior naturalis ad. Mei una fructum tanquam student existam quietem seorsim aut. Essentiae denegante perceptio distinguo realitate vox hos. Negat mundo neque ii co modos gi. Nec vel non etsi duas rem dari regi adeo casu. Capax seu ausit dum cum fidei sexta. Est suo apud fal quam sola idem lus. In complexus importare praefatio tangantur ac de." + + "\n\n" + + "Revocari in sessione me innotuit se gi. Adhuc solum fas porro nec ubi majus non. Autem falso harum si de certo pauca mundo. Quarta at to habeam sequor at nomine deinde. Nam imaginatio formaliter proficisci continetur sua cujuslibet scripturas. Materia proxime ineptum vox tam. Usu digna situs ausit vulgo sed eam. Propter pretium vox insuper sit possunt finitae." + + "\n\n" + + "Attigi aetate jam nullis ubi falsae terram realem. Rei jam discrepant iii aliquoties qua falsitatis mortalibus. Re im de terram gi omnium doctus vestro sensum. Etc persuasi monendos duo dedissem mutuatis ens hoc videamus. Fuse his male cap tria tam poni. Ibi nosse sanae agi satis ego ego. Ego dignum lumine illico debere hoc." + + "\n\n" + + "Putarem quodque tamquam ii ob an deumque. Fuerint judicia me assequi sapores ab verarum. Veniebant sex videbatur assignare eas una corporeis alligatus. Oculis nondum fusius sub ens urgeat fuerit rea sum. Eo ex deumque reddere publice ea similes attinet. Sensuum ac gi fallant incipit. Nullis carnem tam fal existi haereo mei sacras ipsius. Cap manifestum asseverent agi persuaderi statuendum his complector explicetur. Passim vestes maxime pulses animus id ii. Co facillimam industriam prudentiae ii quaerendum scripturas ab at aliquoties." + + "\n\n" + + "Usitate deo dicitur ibi seorsim ubi. Ageretur sum mutuatur acceptis meditari qua loquebar. Restat dicunt postea ac si capram me primam. Cogitare ad ex ab gradatim tractare et. Tes vox persuasi vel nihildum impellit effectus. Fallit dubias liceat qualem ii ut. Acquiro liquida hominem de viderer an pugnare ut ac. Vigilantes necessario ex divisibile intellectu co in cohibendam necessitas re." + + "\n\n" + + "Dubitem videmur usitate itemque ob afferri de. Ac in obfirmata existeret at devenimus ii affirmare. Sic ullos vel fidam sequi quasi. Unde fiat iis hos sunt tot meas quum. Ima corporibus tot efficiente sufficeret non viderentur distinctae vos. Ideo dei quis pla sive haec foco fore. Mox otii aspi iste imo hos ipsi. Earumdem videatur eam hae sui agnoscam. Creatione co corporeis consistat at conservat corporeas. Imo nul pro rom talis sonos fieri."; + + public FakeDataService() { + mActors = generateUsers(); + } + + @NonNull + @Override + public Single> getAllActors() { + return Single.just(mActors) + .delay(new Random().nextInt(5), TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()); + } + + @NonNull + @Override + public Single getActorDetail(final long id) { + return Single.fromEmitter( + new Action1>() { + @Override + public void call(SingleEmitter emitter) { + Actor actor = null; + + final List actors = new ArrayList<>(mActors); + for (final Actor a : actors) { + if (id == a.getId()) { + actor = a; + break; + } + } + + if (actor == null) { + emitter.onError(new Resources.NotFoundException("Actor not found")); + } else { + emitter.onSuccess(new ActorDetail(actor.getName(), "1.1.1990 in Fake city", INFO)); + } + } + }) + .delay(5, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()); + } + + @NonNull + private List generateUsers() { + final String[] names = new String[]{ + "Elfrieda Hutchcraft", + "Shane Gilmour", + "Arthur Reep", + "Minna Keirn", + "Nicki Riner", + "Kiyoko Campanella", + "Regan Gasper", + "Latosha Peel", + "Sabra Stefanski", + "Ja Royster", + "Debora High", + "Jutta Fredricks", + "Mariana Mangan", + "Teresa Nantz", + "Renea Reeb", + "Damon Roseman", + "Arnetta Higgenbotham", + "Laverne Talbott", + "Royce Coppock", + "Joey Mcchristian", + "Tara Nedd", + "Dominic Gossard", + "Meghan Brucker", + "Sabrina Marine", + "Blake Burnett", + "Valerie Suydam", + "Jennette Bunce", + "Winnie Marie", + "Caprice Siegler", + "Janine Schmalz", + "Lane Mcmurry", + "Lessie Vetrano", + "Chanell Bosket", + "Blossom Tynes", + "Augustine Broadbent", + "Carli Branson", + "Kasi Sletten", + "Sabine Lipton", + "Louisa Emmert", + "Robbi Mcglynn", + "Alvaro Mae", + "Ora Manigo", + "Buster Campfield", + "Trinity Muldrew", + "Jonathan Groat", + "Vania Perlmutter", + "Arica Clutts", + "Alexander Lacasse", + "Zulma Aasen", + "Adria Beatrice" + }; + + final List actors = new ArrayList<>(names.length); + + for (int i = 0; i < names.length; i++) { + actors.add(new Actor(i, names[i])); + } + + return actors; + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/OnlineDataService.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/OnlineDataService.java new file mode 100644 index 0000000..030d79d --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/OnlineDataService.java @@ -0,0 +1,57 @@ +package eu.f3rog.blade.sample.mvp.di.module.data; + + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import eu.f3rog.blade.sample.mvp.di.module.data.api.ActorApi; +import eu.f3rog.blade.sample.mvp.di.module.data.api.JsonActor; +import eu.f3rog.blade.sample.mvp.di.module.data.api.JsonActorInfo; +import eu.f3rog.blade.sample.mvp.model.Actor; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import eu.f3rog.blade.sample.mvp.service.DataService; +import rx.Single; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +/* package */ class OnlineDataService implements DataService { + + @NonNull + private final ActorApi mActorApi; + + public OnlineDataService(@NonNull final ActorApi actorApi) { + mActorApi = actorApi; + } + + @NonNull + @Override + public Single> getAllActors() { + return mActorApi.getAllActors() + .subscribeOn(Schedulers.io()) + .map(new Func1, List>() { + @Override + public List call(@NonNull final List jsonActors) { + final List actors = new ArrayList<>(); + for (final JsonActor a : jsonActors) { + actors.add(new Actor(a.id, a.name)); + } + return actors; + } + }); + } + + @NonNull + @Override + public Single getActorDetail(final long id) { + return mActorApi.getActorInfo(id) + .subscribeOn(Schedulers.io()) + .map(new Func1() { + @Override + public ActorDetail call(@NonNull final JsonActorInfo a) { + return new ActorDetail(a.birthName, a.birthDate, a.bio); + } + }); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/PicassoImageLoader.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/PicassoImageLoader.java new file mode 100644 index 0000000..4d6b403 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/PicassoImageLoader.java @@ -0,0 +1,32 @@ +package eu.f3rog.blade.sample.mvp.di.module.data; + + +import android.content.Context; +import android.support.annotation.NonNull; +import android.widget.ImageView; + +import com.jakewharton.picasso.OkHttp3Downloader; +import com.squareup.picasso.Picasso; + +import eu.f3rog.blade.sample.mvp.service.ImageLoader; +import okhttp3.OkHttpClient; + +/* package */ class PicassoImageLoader implements ImageLoader { + + @NonNull + private final Context mAppContext; + + public PicassoImageLoader(@NonNull final Context appContext, + @NonNull final OkHttpClient httpClient) { + + mAppContext = appContext; + Picasso.Builder builder = new Picasso.Builder(appContext) + .downloader(new OkHttp3Downloader(httpClient)); + Picasso.setSingletonInstance(builder.build()); + } + + @Override + public void load(@NonNull final String url, @NonNull final ImageView into) { + Picasso.with(mAppContext).load(url).into(into); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/ActorApi.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/ActorApi.java new file mode 100644 index 0000000..c412c0f --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/ActorApi.java @@ -0,0 +1,22 @@ +package eu.f3rog.blade.sample.mvp.di.module.data.api; + + +import java.util.List; + +import retrofit2.http.GET; +import retrofit2.http.Path; +import rx.Single; + +/** + * A mock api that is created via apiari.io + */ +public interface ActorApi { + + String ENDPOINT = "http://private-d2a21-blade2.apiary-mock.com/"; + + @GET("actors") + Single> getAllActors(); + + @GET("actor/{id}") + Single getActorInfo(@Path("id") final long id); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/JsonActor.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/JsonActor.java new file mode 100644 index 0000000..82e469f --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/JsonActor.java @@ -0,0 +1,7 @@ +package eu.f3rog.blade.sample.mvp.di.module.data.api; + +public final class JsonActor { + + public long id; + public String name; +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/JsonActorInfo.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/JsonActorInfo.java new file mode 100644 index 0000000..56b5ded --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/data/api/JsonActorInfo.java @@ -0,0 +1,8 @@ +package eu.f3rog.blade.sample.mvp.di.module.data.api; + +public final class JsonActorInfo { + + public String birthName; + public String birthDate; + public String bio; +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/ActorListPresenterImpl.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/ActorListPresenterImpl.java new file mode 100644 index 0000000..fae557c --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/ActorListPresenterImpl.java @@ -0,0 +1,96 @@ +package eu.f3rog.blade.sample.mvp.di.module.presenter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.List; + +import eu.f3rog.blade.sample.mvp.model.Actor; +import eu.f3rog.blade.sample.mvp.presenter.ActorListPresenter; +import eu.f3rog.blade.sample.mvp.service.DataService; +import eu.f3rog.blade.sample.mvp.view.ActorListView; +import rx.SingleSubscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; + +/* package */ final class ActorListPresenterImpl extends ActorListPresenter { + + @NonNull + private final DataService mDataService; + + @Nullable + private Subscription mSubscription; + @Nullable + private List mLoadedActors; + @Nullable + private String mErrorMessage; + + public ActorListPresenterImpl(@NonNull final DataService dataService) { + this.mDataService = dataService; + } + + @Override + public void onCreate(@Nullable Object state) { + super.onCreate(state); + + if (mLoadedActors == null) { + startLoading(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mSubscription != null && !mSubscription.isUnsubscribed()) { + mSubscription.unsubscribe(); + mSubscription = null; + } + } + + private void startLoading() { + mLoadedActors = null; + mErrorMessage = null; + mSubscription = mDataService.getAllActors() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new SingleSubscriber>() { + @Override + public void onSuccess(@NonNull final List actors) { + mLoadedActors = actors; + showIn(getView()); + } + + @Override + public void onError(Throwable error) { + mErrorMessage = error.getMessage(); + showIn(getView()); + } + }); + } + + private void showIn(@Nullable final ActorListView view) { + if (view == null) { + return; + } + + if (mErrorMessage != null) { + view.showError(mErrorMessage); + } else if (mLoadedActors != null) { + view.show(mLoadedActors); + } else { + view.showProgress(); + } + } + + @Override + public void onBind(@NonNull ActorListView view) { + super.onBind(view); + showIn(view); + } + + @Override + public void onActorSelected(@NonNull final Actor actor) { + if (getView() != null) { + getView().gotoActorDetail(actor); + } + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/ActorPresenterImpl.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/ActorPresenterImpl.java new file mode 100644 index 0000000..ac68500 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/ActorPresenterImpl.java @@ -0,0 +1,90 @@ +package eu.f3rog.blade.sample.mvp.di.module.presenter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import blade.State; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import eu.f3rog.blade.sample.mvp.presenter.ActorPresenter; +import eu.f3rog.blade.sample.mvp.service.DataService; +import eu.f3rog.blade.sample.mvp.view.ActorView; +import rx.SingleSubscriber; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; + +/* package */ final class ActorPresenterImpl extends ActorPresenter { + + @NonNull + private final DataService mDataService; + + @State + long mActorId; + + @Nullable + private Subscription mSubscription; + @Nullable + private ActorDetail mLoadedActorDetail; + @Nullable + private String mErrorMessage; + + public ActorPresenterImpl(@NonNull final DataService dataService) { + this.mDataService = dataService; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mSubscription != null && !mSubscription.isUnsubscribed()) { + mSubscription.unsubscribe(); + mSubscription = null; + } + } + + private void startLoading() { + mLoadedActorDetail = null; + mErrorMessage = null; + mSubscription = mDataService.getActorDetail(mActorId) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new SingleSubscriber() { + @Override + public void onSuccess(@NonNull final ActorDetail actorDetail) { + mLoadedActorDetail = actorDetail; + showIn(getView()); + } + + @Override + public void onError(Throwable error) { + mErrorMessage = error.getMessage(); + showIn(getView()); + } + }); + } + + private void showIn(@Nullable final ActorView view) { + if (view == null) { + return; + } + + if (mErrorMessage != null) { + view.showError(mErrorMessage); + } else if (mLoadedActorDetail != null) { + view.show(mLoadedActorDetail); + } else { + view.showProgress(); + } + } + + @Override + public void onBind(@NonNull ActorView view) { + super.onBind(view); + showIn(view); + } + + @Override + public void setActorId(final long id) { + if (mActorId != id) { + mActorId = id; + startLoading(); + } + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/PresenterModule.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/PresenterModule.java new file mode 100644 index 0000000..b2a4e32 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/module/presenter/PresenterModule.java @@ -0,0 +1,25 @@ +package eu.f3rog.blade.sample.mvp.di.module.presenter; + +import android.support.annotation.NonNull; + +import dagger.Module; +import dagger.Provides; +import eu.f3rog.blade.sample.mvp.service.DataService; +import eu.f3rog.blade.sample.mvp.presenter.ActorListPresenter; +import eu.f3rog.blade.sample.mvp.presenter.ActorPresenter; +import rx.Scheduler; + + +@Module +public class PresenterModule { + + @Provides + public ActorListPresenter provideActorListPresenter(@NonNull final DataService dataService) { + return new ActorListPresenterImpl(dataService); + } + + @Provides + public ActorPresenter provideActorPresenter(@NonNull final DataService dataService) { + return new ActorPresenterImpl(dataService); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/q/SchedulerType.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/q/SchedulerType.java deleted file mode 100644 index 228cda6..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/di/q/SchedulerType.java +++ /dev/null @@ -1,16 +0,0 @@ -package eu.f3rog.blade.sample.mvp.di.q; - -import javax.inject.Qualifier; - -public interface SchedulerType { - - @Qualifier - @interface Main { - } - - @Qualifier - @interface Task { - } - -} - diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/Actor.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/Actor.java new file mode 100644 index 0000000..48b4cfe --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/Actor.java @@ -0,0 +1,45 @@ +package eu.f3rog.blade.sample.mvp.model; + + +import android.support.annotation.NonNull; + +public final class Actor { + + private final long mId; + @NonNull + private final String mName; + + public Actor(final long id, + @NonNull final String name) { + mId = id; + mName = name; + } + + public long getId() { + return mId; + } + + @NonNull + public String getName() { + return mName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Actor actor = (Actor) o; + + if (mId != actor.mId) return false; + return mName.equals(actor.mName); + + } + + @Override + public int hashCode() { + int result = (int) (mId ^ (mId >>> 32)); + result = 31 * result + mName.hashCode(); + return result; + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/ActorDetail.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/ActorDetail.java new file mode 100644 index 0000000..de670d6 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/ActorDetail.java @@ -0,0 +1,65 @@ +package eu.f3rog.blade.sample.mvp.model; + + +import android.support.annotation.NonNull; + +public final class ActorDetail { + + @NonNull + private final String mBirthName; + @NonNull + private final String mBirthDate; + @NonNull + private final String mBio; + + public ActorDetail(@NonNull final String birthName, + @NonNull final String birthDate, + @NonNull final String bio) { + mBirthName = birthName; + mBirthDate = birthDate; + mBio = bio; + } + + @NonNull + public String getBirthName() { + return mBirthName; + } + + @NonNull + public String getBirthDate() { + return mBirthDate; + } + + @NonNull + public String getBio() { + return mBio; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ActorDetail that = (ActorDetail) o; + + if (!mBirthName.equals(that.mBirthName)) return false; + if (!mBirthDate.equals(that.mBirthDate)) return false; + return mBio.equals(that.mBio); + + } + + @Override + public int hashCode() { + int result = mBirthName.hashCode(); + result = 31 * result + mBirthDate.hashCode(); + result = 31 * result + mBio.hashCode(); + return result; + } + + @Override + public String toString() { + return "Birth Name: " + mBirthName + + "\n\nBirth Date: " + mBirthDate + + "\n\nBio: " + mBio; + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/Data.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/Data.java deleted file mode 100644 index 0560f63..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/model/Data.java +++ /dev/null @@ -1,56 +0,0 @@ -package eu.f3rog.blade.sample.mvp.model; - - -public class Data { - - private final int mWait; - private final int mCount; - private final long mId; - private final String mText; - - public Data(long id, int count, int wait, String text) { - mId = id; - mCount = count; - mWait = wait; - mText = text; - } - - public long getId() { - return mId; - } - - public int getWait() { - return mWait; - } - - public int getCount() { - return mCount; - } - - public String getText() { - return mText; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Data data = (Data) o; - - if (mWait != data.mWait) return false; - if (mId != data.mId) return false; - if (mCount != data.mCount) return false; - return mText != null ? mText.equals(data.mText) : data.mText == null; - - } - - @Override - public int hashCode() { - int result = mWait; - result = 31 * result + (int) (mId ^ (mId >>> 32)); - result = 31 * result + mCount; - result = 31 * result + (mText != null ? mText.hashCode() : 0); - return result; - } -} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/ActorListPresenter.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/ActorListPresenter.java new file mode 100644 index 0000000..88c4187 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/ActorListPresenter.java @@ -0,0 +1,13 @@ +package eu.f3rog.blade.sample.mvp.presenter; + + +import android.support.annotation.NonNull; + +import blade.mvp.BasePresenter; +import eu.f3rog.blade.sample.mvp.model.Actor; +import eu.f3rog.blade.sample.mvp.view.ActorListView; + +public abstract class ActorListPresenter extends BasePresenter { + + public abstract void onActorSelected(@NonNull final Actor actor); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/ActorPresenter.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/ActorPresenter.java new file mode 100644 index 0000000..0b61e0b --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/ActorPresenter.java @@ -0,0 +1,10 @@ +package eu.f3rog.blade.sample.mvp.presenter; + + +import blade.mvp.BasePresenter; +import eu.f3rog.blade.sample.mvp.view.ActorView; + +public abstract class ActorPresenter extends BasePresenter { + + public abstract void setActorId(final long id); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/DataPresenter.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/DataPresenter.java deleted file mode 100644 index 4d97413..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/presenter/DataPresenter.java +++ /dev/null @@ -1,86 +0,0 @@ -package eu.f3rog.blade.sample.mvp.presenter; - -import android.support.annotation.NonNull; - -import java.util.concurrent.TimeUnit; - -import blade.State; -import blade.mvp.BasePresenter; -import eu.f3rog.blade.sample.mvp.model.Data; -import eu.f3rog.blade.sample.mvp.ui.view.IDataView; -import rx.Observable; -import rx.Scheduler; -import rx.functions.Action1; -import rx.functions.Func1; - -/** - * Class {@link DataPresenter} - * - * @author FrantisekGazo - */ -public class DataPresenter extends BasePresenter { - - private final Scheduler mainScheduler; - private final Scheduler taskScheduler; - private Data mData; - @State - String mLoadedValue; - - public DataPresenter(Scheduler mainScheduler, Scheduler taskScheduler) { - this.mainScheduler = mainScheduler; - this.taskScheduler = taskScheduler; - } - - @Override - public void onBind(@NonNull IDataView view) { - super.onBind(view); - - if (mLoadedValue != null) { - view.showValue(mLoadedValue); - } else { - view.showProgress(); - } - } - - private void startFakeLoading() { - mLoadedValue = null; - Observable.range(0, mData.getCount() + 1) - .delay(mData.getWait(), TimeUnit.SECONDS) - .delay(new Func1>() { - @Override - public Observable call(Integer integer) { - return Observable.timer(integer, TimeUnit.SECONDS); - } - }) - .map(new Func1() { - @Override - public String call(Integer integer) { - return integer < mData.getCount() ? String.valueOf(mData.getCount() - integer) : mData.getText(); - } - }) - .subscribeOn(taskScheduler) - .observeOn(mainScheduler) - .subscribe(new Action1() { - @Override - public void call(String s) { - show(s); - } - }); - } - - private void show(String value) { - mLoadedValue = value; - // show result - if (getView() != null) { - getView().showValue(mLoadedValue); - } - } - - public void onViewCreated(Data data) { - if (mData == null) { - mData = data; - - startFakeLoading(); - } - } -} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/service/DataService.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/service/DataService.java new file mode 100644 index 0000000..f8d68ca --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/service/DataService.java @@ -0,0 +1,19 @@ +package eu.f3rog.blade.sample.mvp.service; + +import android.support.annotation.NonNull; + +import java.util.List; + +import eu.f3rog.blade.sample.mvp.model.Actor; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import rx.Single; + + +public interface DataService { + + @NonNull + Single> getAllActors(); + + @NonNull + Single getActorDetail(final long id); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/service/ImageLoader.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/service/ImageLoader.java new file mode 100644 index 0000000..0c3f3e5 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/service/ImageLoader.java @@ -0,0 +1,10 @@ +package eu.f3rog.blade.sample.mvp.service; + + +import android.support.annotation.NonNull; +import android.widget.ImageView; + +public interface ImageLoader { + + void load(@NonNull String url, @NonNull ImageView into); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/App.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/App.java new file mode 100644 index 0000000..5f06be5 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/App.java @@ -0,0 +1,15 @@ +package eu.f3rog.blade.sample.mvp.ui; + +import android.app.Application; + +import eu.f3rog.blade.sample.mvp.di.component.Component; + + +public final class App extends Application { + + @Override + public void onCreate() { + super.onCreate(); + Component.initAppComponent(this); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorFragmentActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorFragmentActivity.java new file mode 100644 index 0000000..d033286 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorFragmentActivity.java @@ -0,0 +1,42 @@ +package eu.f3rog.blade.sample.mvp.ui.activity; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; + +import blade.Blade; +import blade.Extra; +import blade.F; +import butterknife.ButterKnife; +import eu.f3rog.blade.sample.R; + + +/** + * Class {@link ActorFragmentActivity} + * + * @author FrantisekGazo + */ +@Blade +public final class ActorFragmentActivity + extends AppCompatActivity { + + @Extra + long mActorId; + @Extra + String mActorName; + + @Override + protected void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.mvp_activity_actor_fragment); + ButterKnife.bind(this); + + setTitle(mActorName); + + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction() + .replace(R.id.frag, F.newActorFragment(mActorId)) + .commit(); + } + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorViewActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorViewActivity.java new file mode 100644 index 0000000..4c08523 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorViewActivity.java @@ -0,0 +1,42 @@ +package eu.f3rog.blade.sample.mvp.ui.activity; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import blade.Blade; +import blade.Extra; +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.f3rog.blade.sample.R; + + +/** + * Class {@link ActorViewActivity} + * + * @author FrantisekGazo + */ +@Blade +public final class ActorViewActivity + extends AppCompatActivity { + + @Extra + long mActorId; + @Extra + String mActorName; + + @BindView(R.id.view_actor) + View mDetailView; + + @Override + protected void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.mvp_activity_actor_view); + ButterKnife.bind(this); + + setTitle(mActorName); + + mDetailView.setTag(mActorId); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorsActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorsActivity.java new file mode 100644 index 0000000..f8d7bb5 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/ActorsActivity.java @@ -0,0 +1,109 @@ +package eu.f3rog.blade.sample.mvp.ui.activity; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import java.util.List; + +import javax.inject.Inject; + +import blade.Blade; +import blade.Extra; +import blade.F; +import blade.I; +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.f3rog.blade.sample.R; +import eu.f3rog.blade.sample.mvp.di.component.Component; +import eu.f3rog.blade.sample.mvp.model.Actor; +import eu.f3rog.blade.sample.mvp.presenter.ActorListPresenter; +import eu.f3rog.blade.sample.mvp.ui.adapter.ActorAdapter; +import eu.f3rog.blade.sample.mvp.view.ActorListView; + + +/** + * Class {@link ActorsActivity} + * + * @author FrantisekGazo + */ +@Blade +public final class ActorsActivity + extends AppCompatActivity + implements ActorListView, ActorAdapter.OnActorClickListener { + + public enum DetailType { + DIALOG_FRAG, ACTIVITY_WITH_FRAG, ACTIVITY_WITH_VIEW + } + + @Extra + DetailType mDetailType; + + @BindView(android.R.id.text1) + TextView mText; + @BindView(R.id.rv_actors) + RecyclerView mRecyclerView; + + @Inject + ActorListPresenter mPresenter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.mvp_activity_actors); + ButterKnife.bind(this); + setTitle("Actors"); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + + Component.forApp().inject(this); + } + + @Override + public void showProgress() { + mText.setText("Loading..."); + + mRecyclerView.setVisibility(View.GONE); + mText.setVisibility(View.VISIBLE); + } + + @Override + public void showError(@NonNull final String errorMessage) { + mText.setText("Error: " + errorMessage); + + mRecyclerView.setVisibility(View.GONE); + mText.setVisibility(View.VISIBLE); + } + + @Override + public void show(@NonNull final List actors) { + final ActorAdapter adapter = new ActorAdapter(this, actors, this); + mRecyclerView.setAdapter(adapter); + + mText.setVisibility(View.GONE); + mRecyclerView.setVisibility(View.VISIBLE); + } + + @Override + public void gotoActorDetail(@NonNull final Actor actor) { + switch (mDetailType) { + case DIALOG_FRAG: + F.newActorDialogFragment(actor.getId(), actor.getName()).show(getSupportFragmentManager(), "dialog-tag"); + break; + case ACTIVITY_WITH_FRAG: + I.startActorFragmentActivity(this, actor.getId(), actor.getName()); + break; + case ACTIVITY_WITH_VIEW: + I.startActorViewActivity(this, actor.getId(), actor.getName()); + break; + } + } + + @Override + public void onClick(@NonNull final Actor actor) { + mPresenter.onActorSelected(actor); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/MvpMainActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/MvpMainActivity.java new file mode 100644 index 0000000..5ac7a0c --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/MvpMainActivity.java @@ -0,0 +1,38 @@ +package eu.f3rog.blade.sample.mvp.ui.activity; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import blade.Blade; +import blade.I; +import butterknife.ButterKnife; +import butterknife.OnClick; +import eu.f3rog.blade.sample.R; + + +@Blade +public final class MvpMainActivity + extends AppCompatActivity { + + @OnClick(R.id.btn_1) + void showExample1() { + I.startActorsActivity(this, ActorsActivity.DetailType.DIALOG_FRAG); + } + + @OnClick(R.id.btn_2) + void showExample2() { + I.startActorsActivity(this, ActorsActivity.DetailType.ACTIVITY_WITH_FRAG); + } + + @OnClick(R.id.btn_3) + void showExample3() { + I.startActorsActivity(this, ActorsActivity.DetailType.ACTIVITY_WITH_VIEW); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.mvp_activity_main); + ButterKnife.bind(this); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/TestMvpActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/TestMvpActivity.java deleted file mode 100644 index e9bf96a..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/activity/TestMvpActivity.java +++ /dev/null @@ -1,73 +0,0 @@ -package eu.f3rog.blade.sample.mvp.ui.activity; - -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import javax.inject.Inject; - -import blade.Blade; -import blade.F; -import butterknife.Bind; -import butterknife.ButterKnife; -import butterknife.OnClick; -import eu.f3rog.blade.sample.R; -import eu.f3rog.blade.sample.mvp.di.component.Component; -import eu.f3rog.blade.sample.mvp.model.Data; -import eu.f3rog.blade.sample.mvp.presenter.DataPresenter; -import eu.f3rog.blade.sample.mvp.ui.view.DataView; -import eu.f3rog.blade.sample.mvp.ui.view.IDataView; - - -/** - * Class {@link TestMvpActivity} - * - * @author FrantisekGazo - */ -@Blade -public final class TestMvpActivity - extends AppCompatActivity - implements IDataView { - - @Inject - DataPresenter mPresenter; - - @Bind(R.id.container) - ViewGroup mContainer; - @Bind(R.id.txt_activity_value) - TextView mTextView; - - @OnClick(R.id.btn_show_dialog) - void showDialog() { - F.newTestMvpDialogFragment().show(getSupportFragmentManager(), "mvp-dialog-tag"); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_test_mvp); - ButterKnife.bind(this); - - Component.forApp().inject(this); - - mPresenter.onViewCreated(new Data(123, 10, 1, "Hello")); - -// DataView child = new DataView(this); -// child.setId(12345678); -// mContainer.addView(child); - } - - @Override - public void showValue(String value) { - mTextView.setText(value); - } - - @Override - public void showProgress() { - mTextView.setText("..."); - } - -} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/adapter/ActorAdapter.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/adapter/ActorAdapter.java new file mode 100644 index 0000000..d5a228f --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/adapter/ActorAdapter.java @@ -0,0 +1,54 @@ +package eu.f3rog.blade.sample.mvp.ui.adapter; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +import eu.f3rog.blade.sample.R; +import eu.f3rog.blade.sample.mvp.model.Actor; + + +public final class ActorAdapter + extends RecyclerView.Adapter { + + public interface OnActorClickListener { + void onClick(@NonNull final Actor actor); + } + + @NonNull + private final Context mContext; + @NonNull + private final List mActors; + @NonNull + private final OnActorClickListener mListener; + + public ActorAdapter(@NonNull final Context context, + @NonNull final List actors, + @NonNull final OnActorClickListener listener) { + mContext = context; + mActors = actors; + mListener = listener; + } + + @Override + public ActorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + final View view = LayoutInflater.from(mContext) + .inflate(R.layout.mvp_item_actor, parent, false); + return new ActorViewHolder(view); + } + + @Override + public void onBindViewHolder(ActorViewHolder holder, int position) { + holder.bind(mActors.get(position), mListener); + } + + @Override + public int getItemCount() { + return mActors.size(); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/adapter/ActorViewHolder.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/adapter/ActorViewHolder.java new file mode 100644 index 0000000..b598a1f --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/adapter/ActorViewHolder.java @@ -0,0 +1,44 @@ +package eu.f3rog.blade.sample.mvp.ui.adapter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.f3rog.blade.sample.mvp.model.Actor; + +/* package */ final class ActorViewHolder + extends RecyclerView.ViewHolder + implements View.OnClickListener { + + @BindView(android.R.id.text1) + TextView mName; + + @Nullable + private Actor mActor; + @Nullable + private ActorAdapter.OnActorClickListener mListener; + + public ActorViewHolder(@NonNull final View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + mName.setOnClickListener(this); + } + + public void bind(@NonNull final Actor actor, @Nullable final ActorAdapter.OnActorClickListener listener) { + mActor = actor; + mListener = listener; + + mName.setText(actor.getName()); + } + + @Override + public void onClick(View view) { + if (mActor != null && mListener != null) { + mListener.onClick(mActor); + } + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/TestMvpDialogFragment.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/ActorDialogFragment.java similarity index 54% rename from sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/TestMvpDialogFragment.java rename to sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/ActorDialogFragment.java index bd30f22..53e08a3 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/TestMvpDialogFragment.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/ActorDialogFragment.java @@ -12,60 +12,65 @@ import javax.inject.Inject; +import blade.Arg; import blade.Blade; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import eu.f3rog.blade.sample.R; import eu.f3rog.blade.sample.mvp.di.component.Component; -import eu.f3rog.blade.sample.mvp.model.Data; -import eu.f3rog.blade.sample.mvp.presenter.DataPresenter; -import eu.f3rog.blade.sample.mvp.ui.view.IDataView; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import eu.f3rog.blade.sample.mvp.presenter.ActorPresenter; +import eu.f3rog.blade.sample.mvp.view.ActorView; /** - * Class {@link TestMvpDialogFragment} + * Class {@link ActorDialogFragment} * * @author FrantisekGazo */ @Blade -public final class TestMvpDialogFragment +public final class ActorDialogFragment extends DialogFragment - implements IDataView { + implements ActorView { + + @Arg + long mId; + @Arg + String mName; @Inject - DataPresenter mPresenter; + ActorPresenter mPresenter; - @Bind(android.R.id.text1) + @BindView(android.R.id.text1) TextView mTextView; @NonNull @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_test_mvp, null, false); + public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { + View view = LayoutInflater.from(getContext()).inflate(R.layout.mvp_frag_actor, null, false); ButterKnife.bind(this, view); Component.forApp().inject(this); - mPresenter.onViewCreated(new Data(1, 5, 1, "Done!")); + mPresenter.setActorId(mId); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) - .setTitle("MVP Dialog") + .setTitle(mName) .setView(view); return builder.create(); } @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + public void showProgress() { + mTextView.setText("Loading..."); } @Override - public void showValue(String value) { - mTextView.setText(value); + public void showError(@NonNull final String errorMessage) { + mTextView.setText("Error: " + errorMessage); } @Override - public void showProgress() { - mTextView.setText("..."); + public void show(@NonNull final ActorDetail actorDetail) { + mTextView.setText(actorDetail.toString()); } - } diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/ActorFragment.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/ActorFragment.java new file mode 100644 index 0000000..7addc6f --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/ActorFragment.java @@ -0,0 +1,70 @@ +package eu.f3rog.blade.sample.mvp.ui.fragment; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import javax.inject.Inject; + +import blade.Arg; +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.f3rog.blade.sample.R; +import eu.f3rog.blade.sample.mvp.di.component.Component; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import eu.f3rog.blade.sample.mvp.presenter.ActorPresenter; +import eu.f3rog.blade.sample.mvp.view.ActorView; + +/** + * Class {@link ActorFragment} + * + * @author FrantisekGazo + */ +public final class ActorFragment + extends Fragment + implements ActorView { + + @Arg + long mId; + + @Inject + ActorPresenter mPresenter; + + @BindView(android.R.id.text1) + TextView mTextView; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.mvp_frag_actor, container, false); + } + + @Override + public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + ButterKnife.bind(this, view); + + Component.forApp().inject(this); + mPresenter.setActorId(mId); + } + + @Override + public void showProgress() { + mTextView.setText("Loading..."); + } + + @Override + public void showError(@NonNull final String errorMessage) { + mTextView.setText("Error: " + errorMessage); + } + + @Override + public void show(@NonNull final ActorDetail actorDetail) { + mTextView.setText(actorDetail.toString()); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/TestMvpFragment.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/TestMvpFragment.java deleted file mode 100644 index 4c4193b..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/fragment/TestMvpFragment.java +++ /dev/null @@ -1,61 +0,0 @@ -package eu.f3rog.blade.sample.mvp.ui.fragment; - -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import javax.inject.Inject; - -import butterknife.Bind; -import butterknife.ButterKnife; -import eu.f3rog.blade.sample.R; -import eu.f3rog.blade.sample.mvp.di.component.Component; -import eu.f3rog.blade.sample.mvp.model.Data; -import eu.f3rog.blade.sample.mvp.presenter.DataPresenter; -import eu.f3rog.blade.sample.mvp.ui.view.IDataView; - -/** - * Class {@link TestMvpFragment} - * - * @author FrantisekGazo - */ -public final class TestMvpFragment - extends Fragment - implements IDataView { - - @Inject - DataPresenter mPresenter; - - @Bind(android.R.id.text1) - TextView mTextView; - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_test_mvp, container, false); - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - ButterKnife.bind(this, view); - - Component.forApp().inject(this); - - mPresenter.onViewCreated(new Data(123, 20, 1, "World!")); - } - - @Override - public void showValue(String value) { - mTextView.setText(value); - } - - @Override - public void showProgress() { - mTextView.setText("..."); - } -} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/ActorCustomView.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/ActorCustomView.java new file mode 100644 index 0000000..c36373d --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/ActorCustomView.java @@ -0,0 +1,90 @@ +package eu.f3rog.blade.sample.mvp.ui.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.f3rog.blade.sample.R; +import eu.f3rog.blade.sample.mvp.di.component.Component; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; +import eu.f3rog.blade.sample.mvp.presenter.ActorPresenter; +import eu.f3rog.blade.sample.mvp.view.ActorView; + + +/** + * Class {@link ActorCustomView} + * + * @author FrantisekGazo + */ +public final class ActorCustomView + extends LinearLayout + implements ActorView { + + @BindView(android.R.id.text1) + TextView mText; + @BindView(R.id.progress) + ProgressBar mProgressBar; + + @Inject + ActorPresenter mPresenter; + + public ActorCustomView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ActorCustomView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public ActorCustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + ButterKnife.bind(this); + + mText.setVisibility(GONE); + mProgressBar.setVisibility(GONE); + } + + @Override + public void showProgress() { + mText.setVisibility(GONE); + mProgressBar.setVisibility(VISIBLE); + } + + @Override + public void showError(@NonNull final String errorMessage) { + mText.setText(errorMessage); + + mText.setVisibility(VISIBLE); + mProgressBar.setVisibility(GONE); + } + + @Override + public void show(@NonNull final ActorDetail actorDetail) { + mText.setText(actorDetail.getBio()); + + mText.setVisibility(VISIBLE); + mProgressBar.setVisibility(GONE); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Component.forApp().inject(this); + mPresenter.setActorId((Long) getTag()); + } +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/DataView.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/DataView.java deleted file mode 100644 index 59ad7ed..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/DataView.java +++ /dev/null @@ -1,105 +0,0 @@ -package eu.f3rog.blade.sample.mvp.ui.view; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.os.Parcelable; -import android.util.AttributeSet; -import android.view.View; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -import javax.inject.Inject; - -import butterknife.Bind; -import butterknife.ButterKnife; -import eu.f3rog.blade.sample.R; -import eu.f3rog.blade.sample.mvp.di.component.Component; -import eu.f3rog.blade.sample.mvp.model.Data; -import eu.f3rog.blade.sample.mvp.presenter.DataPresenter; - - -/** - * Class {@link DataView} - * - * @author FrantisekGazo - */ -public final class DataView - extends LinearLayout - implements IDataView { - - @Bind(R.id.value_layout) - View mValueLayout; - @Bind(R.id.txt_value) - TextView mTxtValue; - @Bind(R.id.progress) - ProgressBar mProgressBar; - - @Inject - DataPresenter mPresenter; - - public DataView(Context context) { - super(context); - } - - public DataView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public DataView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public DataView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - ButterKnife.bind(this); - - mValueLayout.setVisibility(GONE); - mProgressBar.setVisibility(GONE); - } - - @Override - public void showProgress() { - mValueLayout.setVisibility(GONE); - mProgressBar.setVisibility(VISIBLE); - } - - @Override - public void showValue(String value) { - mTxtValue.setText(value); - - mValueLayout.setVisibility(VISIBLE); - mProgressBar.setVisibility(GONE); - } - - @Override - protected Parcelable onSaveInstanceState() { - Parcelable parcelable = super.onSaveInstanceState(); - return parcelable; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - super.onRestoreInstanceState(state); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - Component.forApp().inject(this); - - mPresenter.onViewCreated(new Data(123, 5, 1, "Loaded Text")); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - } -} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/IDataView.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/IDataView.java deleted file mode 100644 index cc51a09..0000000 --- a/sample/src/main/java/eu/f3rog/blade/sample/mvp/ui/view/IDataView.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.f3rog.blade.sample.mvp.ui.view; - -import blade.mvp.IView; - - -/** - * Interface {@link IDataView} - * - * @author FrantisekGazo - */ -public interface IDataView extends IView { - - void showValue(String value); - - void showProgress(); - -} - diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/view/ActorListView.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/view/ActorListView.java new file mode 100644 index 0000000..12b2133 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/view/ActorListView.java @@ -0,0 +1,20 @@ +package eu.f3rog.blade.sample.mvp.view; + + +import android.support.annotation.NonNull; + +import java.util.List; + +import blade.mvp.IView; +import eu.f3rog.blade.sample.mvp.model.Actor; + +public interface ActorListView extends IView { + + void showProgress(); + + void showError(@NonNull final String errorMessage); + + void show(@NonNull final List actors); + + void gotoActorDetail(@NonNull final Actor actor); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/mvp/view/ActorView.java b/sample/src/main/java/eu/f3rog/blade/sample/mvp/view/ActorView.java new file mode 100644 index 0000000..17d17a5 --- /dev/null +++ b/sample/src/main/java/eu/f3rog/blade/sample/mvp/view/ActorView.java @@ -0,0 +1,16 @@ +package eu.f3rog.blade.sample.mvp.view; + + +import android.support.annotation.NonNull; + +import blade.mvp.IView; +import eu.f3rog.blade.sample.mvp.model.ActorDetail; + +public interface ActorView extends IView { + + void showProgress(); + + void showError(@NonNull final String errorMessage); + + void show(@NonNull final ActorDetail actorDetail); +} diff --git a/sample/src/main/java/eu/f3rog/blade/sample/parcel/TestParcelActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/parcel/TestParcelActivity.java index 2daeda2..1a18b87 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/parcel/TestParcelActivity.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/parcel/TestParcelActivity.java @@ -11,7 +11,7 @@ import java.util.Random; import blade.Blade; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import eu.f3rog.blade.sample.R; @@ -19,7 +19,7 @@ @Blade public class TestParcelActivity extends AppCompatActivity { - @Bind(R.id.txt_output) + @BindView(R.id.txt_output) TextView mOutput; @Override diff --git a/sample/src/main/java/eu/f3rog/blade/sample/state/TestStateActivity.java b/sample/src/main/java/eu/f3rog/blade/sample/state/TestStateActivity.java index 307b41b..7693159 100644 --- a/sample/src/main/java/eu/f3rog/blade/sample/state/TestStateActivity.java +++ b/sample/src/main/java/eu/f3rog/blade/sample/state/TestStateActivity.java @@ -7,7 +7,7 @@ import blade.Blade; import blade.State; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; import eu.f3rog.blade.sample.R; @@ -19,7 +19,7 @@ public class TestStateActivity extends AppCompatActivity { @State(StringCustomBundler.class) String mText; - @Bind(R.id.txt) + @BindView(R.id.txt) TextView mTextView; @Override diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 812810e..3825430 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -8,6 +8,12 @@ android:padding="@dimen/activity_vertical_margin" tools:context=".MainActivity"> +