diff --git a/.gitignore b/.gitignore index 630efea1..b30419a4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,7 @@ captures/ # Moxy artifacts VERSION artifacts/ -artifacts-maven/ \ No newline at end of file +artifacts-maven/ + +# Mac OS +*.icloud \ No newline at end of file diff --git a/README.md b/README.md index 88628785..ddac588d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,27 @@ -# Moxy -[![Maven Central](https://img.shields.io/maven-central/v/com.arello-mobile/moxy.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.arello-mobile%22%20AND%20(a%3A%22moxy%22%20OR%20a%3A%22moxy-compiler%22%20OR%20a%3A%22moxy-android%22%20OR%20a%3A%22moxy-app-compat%22)) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) +# MoxyX +We added X to the Moxy for make this library coolest. -Moxy is a library that helps to use MVP pattern when you do the Android Application. _Without problems of lifecycle and boilerplate code!_ +[![Maven Central](https://img.shields.io/maven-central/v/tech.schoolhelper/moxy-x.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22tech.schoolhelper%22%20AND%20a:%22moxy-x%22) + +[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-MoxyX-blue.svg?style=flat)](https://android-arsenal.com/details/1/7547) + +[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) + +Moxy is a library that helps to use MVP pattern when you do the Android Application. _Without problems of lifecycle and boilerplate code! The main idea of using Moxy: ![schematic_using](https://habrastorage.org/files/a2e/b51/8b4/a2eb518b465a4df9b47e68794519270d.gif) + See what's happening here in the [wiki](https://github.com/Arello-Mobile/Moxy/wiki). +## Diff between us and root project +1. We support androidX +2. We don't support GLOBAL and WEAK presenter type +3. We have one more strategy than core project, [AddToEndSingleTagStrategy](https://github.com/jordan1997/Moxy/blob/master/moxy/src/main/java/com/arellomobile/mvp/viewstate/strategy/AddToEndSingleTagStrategy.java) + +RoadMap +- [ ] We will change the template for android studio (and Intellij), the templates will create a view interface and activity or fragment into one file, and this file and presenter keep into one package. We will remove template for Java. + ## Capabilities Moxy has a few killer features in other ways: @@ -17,97 +32,104 @@ Moxy has a few killer features in other ways: ## Sample View interface -```java -public interface HelloWorldView extends MvpView { - void showMessage(int message); -} -``` -Presenter -```java -@InjectViewState -public class HelloWorldPresenter extends MvpPresenter { - public HelloWorldPresenter() { - getViewState().showMessage(R.string.hello_world); - } +```kotlin +interface MainView : MvpView { + fun printLog(msg: String) } -``` -View implementation -```java -public class HelloWorldActivity extends MvpAppCompatActivity implements HelloWorldView { +class MainActivity : MvpAppCompatActivity(), MainView { + @InjectPresenter - HelloWorldPresenter mHelloWorldPresenter; - - private TextView mHelloWorldTextView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_hello_world); - - mHelloWorldTextView = ((TextView) findViewById(R.id.activity_hello_world_text_view_message)); + internal lateinit var presenter: MainPresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) } - - @Override - public void showMessage(int message) { - mHelloWorldTextView.setText(message); + + override fun printLog(msg: String) { + Log.e(TAG, "printLog : msg : $msg activity hash code : ${hashCode()}") + } + + companion object { + const val TAG = "MoxyDebug" + } +} +``` +Presenter +```kotlin +@InjectViewState +class MainPresenter : MvpPresenter() { + override fun onFirstViewAttach() { + super.onFirstViewAttach() + Log.e(MainActivity.TAG, "presenter hash code : ${hashCode()}") + viewState.printLog("TEST") } } ``` -[Here](https://github.com/Arello-Mobile/Moxy/tree/master/sample-github) you can see "Github" sample application. +[Here](https://github.com/jordan1997/Moxy/tree/develop/sample-github) you can see "Github" sample application. ## Wiki For all information check [Moxy Wiki](https://github.com/Arello-Mobile/Moxy/wiki) -## Android studio templates -In order to avoid boilerplate code creating for binding activity, fragments and its presentation part, we propose to use Android Studio templates for Moxy. +## Android studio and Intellij templates +**We will change this template in future** +In order to avoid boilerplate code creating for binding activity, fragments and its presentation part, we propose to use Android Studio templates for Moxy. -Templates located in [/moxy-templates](https://github.com/Arello-Mobile/Moxy/tree/master/moxy-templates) +Templates located in [/moxy-templates](https://github.com/jordan1997/Moxy/tree/develop/moxy-templates) ## Links +**Telegram channels from original moxy community** + [Telegram channel (en)](https://telegram.me/moxy_mvp_library)
[Telegram channel (ru)](https://telegram.me/moxy_ru)
[References](https://github.com/Arello-Mobile/Moxy/wiki#references)
[FAQ](https://github.com/Arello-Mobile/Moxy/wiki/FAQ) +## Twitter +For connection with author of this repository use twitter +[Twitter](https://twitter.com/jordan29041997) + ## Integration -Base modules integration: +### Base modules integration: ```groovy -dependencies { - ... - compile 'com.arello-mobile:moxy:1.5.5' - annotationProcessor 'com.arello-mobile:moxy-compiler:1.5.5' -} +implementation 'tech.schoolhelper:moxy-x:1.7.0' +``` +#### Java project +```groovy +annotationProcessor 'tech.schoolhelper:moxy-x-compiler:1.7.0' +``` +#### Kotlin +```groovy +apply plugin: 'kotlin-kapt' ``` +```groovy +kapt 'tech.schoolhelper:moxy-x-compiler:1.7.0' +``` +### Default android module integration For additional base view classes `MvpActivity` and `MvpFragment` add this: ```groovy -dependencies { - ... - compile 'com.arello-mobile:moxy-android:1.5.5' -} +implementation 'tech.schoolhelper:moxy-x-android:1.7.0' ``` -If you are planning to use AppCompat, then you can use `MvpAppCompatActivity` and `MvpAppCompatFragment`. Then add this: +### AppCompat module integration +If you use AppCompat, use `MvpAppCompatActivity` and `MvpAppCompatFragment` add this: ```groovy -dependencies { - ... - compile 'com.arello-mobile:moxy-app-compat:1.5.5' - compile 'com.android.support:appcompat-v7:$support_version' -} +implementation 'tech.schoolhelper:moxy-x-app-compat:1.7.0' ``` -### Kotlin -If you are using kotlin, use `kapt` instead of `provided`/`apt` dependency type: +### AndroidX module integration +If you use AndroidX, use `MvpAppCompatActivity` and `MvpAppCompatFragment` add this: ```groovy -apply plugin: 'kotlin-kapt' - -dependencies { - ... - kapt 'com.arello-mobile:moxy-compiler:1.5.5' -} +implementation 'tech.schoolhelper:moxy-x-androidx:1.7.0' +``` +### AndroidX(Google material) module integration +If you use google material, use `MvpBottomSheetDialogFragment` add this: +```groovy +implementation 'tech.schoolhelper:moxy-x-material:1.7.0' ``` ## ProGuard -Moxy is completely without reflection! No special ProGuard rules required. +MoxyX is completely without reflection! No special ProGuard rules required. ## License ``` diff --git a/build.gradle b/build.gradle index 4b6f1b8b..0a26d37e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.2' + classpath 'com.android.tools.build:gradle:3.2.1' } } @@ -20,20 +20,20 @@ allprojects { } ext { - targetVersionCode = 45 - targetVersionName = "1.5.5" + targetVersionCode = 47 + targetVersionName = "1.7.0" deps = [ android : 'com.google.android:android:1.6_r2', - javapoet : 'com.squareup:javapoet:1.9.0', + javapoet : 'com.squareup:javapoet:1.10.0', junit : 'junit:junit:4.12', mockito : 'org.mockito:mockito-core:1.10.19', truth : 'com.google.truth:truth:0.34', robolectric : 'org.robolectric:robolectric:3.0', - compiletesting: 'com.google.testing.compile:compile-testing:0.11', + compiletesting: 'com.google.testing.compile:compile-testing:0.15', asm : ['org.ow2.asm:asm:6.0', 'org.ow2.asm:asm-util:6.0'], - autoservice : 'com.google.auto.service:auto-service:1.0-rc3', - autocommon : 'com.google.auto:auto-common:0.8', + autoservice : 'com.google.auto.service:auto-service:1.0-rc4', + autocommon : 'com.google.auto:auto-common:0.10', guava : 'com.google.guava:guava:21.0', ] } diff --git a/gradle.properties b/gradle.properties index e7bb7068..d746b04c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,20 +1,21 @@ # Project-wide Gradle settings. - # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. - # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html - # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - -#org.gradle.java.home= \ No newline at end of file +#org.gradle.java.home= +POM_LICENCE_URL=https\://opensource.org/licenses/MIT +POM_LICENCE_NAME=The MIT License (MIT) +POM_SCM_CONNECTION=scm:git@github.com:jordan1997/Moxy.git +POM_URL=https://github.com/jordan1997/Moxy +URL=https://schoolhelper.tech/ +POM_DESCRIPTION=MoxyX, we added X for make this librayr coolest \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2c2792bf..87f8b4ca 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jun 05 00:53:30 NOVT 2018 +#Sat Nov 24 23:06:24 NOVT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/makegpg.sh b/makegpg.sh new file mode 100755 index 00000000..564b3f5f --- /dev/null +++ b/makegpg.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +rm -rf moxy-material/artifacts-maven +rm -rf moxy-compiler/artifacts-maven +rm -rf moxy-app-compat/artifacts-maven +rm -rf moxy-androidx/artifacts-maven +rm -rf moxy-android/artifacts-maven +rm -rf moxy/artifacts-maven + +echo "artifacts was clean" + +./gradlew moxy-x:assemble +./gradlew moxy-x-android:assemble +./gradlew moxy-x-androidx:assemble +./gradlew moxy-x-app-compat:assemble +./gradlew moxy-x-compile:assemble +./gradlew moxy-x-material:assemble + +echo "artifacts built" + +cd moxy/artifacts-maven + +gpg2 -ab moxy-x-javadoc.jar +gpg2 -ab moxy-x-sources.jar +gpg2 -ab moxy-x.jar +gpg2 -ab moxy-x.pom + +echo "gpg for moxy-x built" + +cd .. && cd .. + +cd moxy-android/artifacts-maven + +gpg2 -ab moxy-x-android-javadoc.jar +gpg2 -ab moxy-x-android-sources.jar +gpg2 -ab moxy-x-android.jar +gpg2 -ab moxy-x-android.pom + +echo "gpg for moxy-x-android built" + +cd .. && cd .. + +cd moxy-androidx/artifacts-maven + +gpg2 -ab moxy-x-androidx-javadoc.jar +gpg2 -ab moxy-x-androidx-sources.jar +gpg2 -ab moxy-x-androidx.jar +gpg2 -ab moxy-x-androidx.pom + +echo "gpg for moxy-x-androidX built" + +cd .. && cd .. + +cd moxy-app-compat/artifacts-maven + +gpg2 -ab moxy-x-app-compat-javadoc.jar +gpg2 -ab moxy-x-app-compat-sources.jar +gpg2 -ab moxy-x-app-compat.jar +gpg2 -ab moxy-x-app-compat.pom + +echo "gpg for moxy-x-app-compat built" + +cd .. && cd .. + +cd moxy-compiler/artifacts-maven + +gpg2 -ab moxy-x-compiler-javadoc.jar +gpg2 -ab moxy-x-compiler-sources.jar +gpg2 -ab moxy-x-compiler.jar +gpg2 -ab moxy-x-compiler.pom + +echo "gpg for moxy-x-app-compiler built" + +cd .. && cd .. + +cd moxy-material/artifacts-maven + +gpg2 -ab moxy-x-material-javadoc.jar +gpg2 -ab moxy-x-material-sources.jar +gpg2 -ab moxy-x-material.jar +gpg2 -ab moxy-x-material.pom + +echo "gpg for moxy-x-material built" \ No newline at end of file diff --git a/moxy-android/build.gradle b/moxy-android/build.gradle index 226b07e9..ec4eac42 100644 --- a/moxy-android/build.gradle +++ b/moxy-android/build.gradle @@ -5,8 +5,8 @@ apply plugin: 'maven' ext { publishDir = 'artifacts' publishMavenDir = 'artifacts-maven' - targetGroupId = 'com.arello-mobile' - targetArtefactId = 'moxy-android' + targetGroupId = 'tech.schoolhelper' + targetArtefactId = 'moxy-x-android' targetArtefactIdDev = 'moxy-android-dev' versionFile = 'VERSION' } @@ -73,15 +73,15 @@ def getBasePom(String libName) { version targetVersionName name 'Moxy-android' modelVersion = '4.0.0' - description = 'Arello-Mobile MVP library for Android' - url = 'https://www.arello-mobile.com/' + description = POM_DESCRIPTION + url = 'URL' packaging = 'jar' inceptionYear '2016' licenses { license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + name POM_LICENCE_NAME + url POM_LICENCE_URL distribution 'repo' } } @@ -99,12 +99,18 @@ def getBasePom(String libName) { organization = 'Arello Mobile' organizationUrl 'http://www.arello-mobile.com/' } + developer { + name 'Vova Stelmashchuk' + email 'vovochkastelmashchuk@gmail.com' + organization = 'SchoolHelper' + organizationUrl URL + } } scm { - connection 'scm:git@github.com:Arello-Mobile/Moxy.git' - developerConnection 'scm:git@github.com:Arello-Mobile/Moxy.git' - url 'https://github.com/Arello-Mobile/Moxy' + connection POM_SCM_CONNECTION + developerConnection POM_SCM_CONNECTION + url POM_URL } }.withXml { asNode().appendNode('packaging', 'jar') @@ -142,8 +148,6 @@ task copyJars { } - - task createPomXml { doLast { // creating local pom @@ -235,12 +239,12 @@ copyVersion.dependsOn createPomXml assemble.dependsOn copyVersion dependencies { - compile project(':moxy') + implementation project(':moxy-x') compileOnly deps.android compileOnly project(':stub-android') - javadocDeps project(':moxy') + javadocDeps project(':moxy-x') javadocDeps deps.android javadocDeps project(':stub-android') } \ No newline at end of file diff --git a/moxy-androidx-sample/.gitignore b/moxy-androidx-sample/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/moxy-androidx-sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/moxy-androidx-sample/build.gradle b/moxy-androidx-sample/build.gradle new file mode 100644 index 00000000..f9313367 --- /dev/null +++ b/moxy-androidx-sample/build.gradle @@ -0,0 +1,54 @@ +buildscript { + ext.kotlin_version = '1.3.21' + repositories { + google() + jcenter() + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 28 + + defaultConfig { + applicationId "example.com.moxy_androidx_sapmle" + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation "com.google.android.material:material:1.0.0" + + //uncomment for test module + /*implementation project(':moxy-x') + implementation project(':moxy-x-androidx') + kapt project(':moxy-x-compiler')*/ + + def moxyVersion = '1.6.0-beta' + implementation "tech.schoolhelper:moxy-x:$moxyVersion" + implementation "tech.schoolhelper:moxy-x-androidx:$moxyVersion" + kapt "tech.schoolhelper:moxy-x-compiler:$moxyVersion" + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} diff --git a/moxy-androidx-sample/gradle.properties b/moxy-androidx-sample/gradle.properties new file mode 100644 index 00000000..b766318a --- /dev/null +++ b/moxy-androidx-sample/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +#org.gradle.java.home= +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file diff --git a/moxy-androidx-sample/proguard-rules.pro b/moxy-androidx-sample/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/moxy-androidx-sample/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/moxy-androidx-sample/src/main/AndroidManifest.xml b/moxy-androidx-sample/src/main/AndroidManifest.xml new file mode 100644 index 00000000..7269fa23 --- /dev/null +++ b/moxy-androidx-sample/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/moxy-androidx-sample/src/main/kotlin/example/com/androidxsample/MainActivity.kt b/moxy-androidx-sample/src/main/kotlin/example/com/androidxsample/MainActivity.kt new file mode 100644 index 00000000..e0e124bc --- /dev/null +++ b/moxy-androidx-sample/src/main/kotlin/example/com/androidxsample/MainActivity.kt @@ -0,0 +1,32 @@ +package example.com.androidxsample + +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import com.arellomobile.mvp.MvpAppCompatActivity +import com.arellomobile.mvp.MvpView +import com.arellomobile.mvp.presenter.InjectPresenter +import example.com.androidxsample.R + +interface MainView : MvpView { + fun printLog(msg: String) +} + +class MainActivity : MvpAppCompatActivity(), MainView { + + @InjectPresenter + internal lateinit var presenter: MainPresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + } + + override fun printLog(msg: String) { + Log.e(TAG, "printLog : msg : $msg activity hash code : ${hashCode()}") + } + + companion object { + const val TAG = "MoxyDebug" + } +} diff --git a/moxy-androidx-sample/src/main/kotlin/example/com/androidxsample/MainPresenter.kt b/moxy-androidx-sample/src/main/kotlin/example/com/androidxsample/MainPresenter.kt new file mode 100644 index 00000000..a550a85e --- /dev/null +++ b/moxy-androidx-sample/src/main/kotlin/example/com/androidxsample/MainPresenter.kt @@ -0,0 +1,14 @@ +package example.com.androidxsample + +import android.util.Log +import com.arellomobile.mvp.InjectViewState +import com.arellomobile.mvp.MvpPresenter + +@InjectViewState +class MainPresenter : MvpPresenter() { + override fun onFirstViewAttach() { + super.onFirstViewAttach() + Log.e(MainActivity.TAG, "presenter hash code : ${hashCode()}") + viewState.printLog("TEST") + } +} \ No newline at end of file diff --git a/moxy-androidx-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/moxy-androidx-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c3903edf --- /dev/null +++ b/moxy-androidx-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/moxy-androidx-sample/src/main/res/drawable/ic_launcher_background.xml b/moxy-androidx-sample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..9d478f4c --- /dev/null +++ b/moxy-androidx-sample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/moxy-androidx-sample/src/main/res/layout/activity_main.xml b/moxy-androidx-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..1692a83d --- /dev/null +++ b/moxy-androidx-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/moxy-androidx-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/moxy-androidx-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..bbd3e021 --- /dev/null +++ b/moxy-androidx-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/moxy-androidx-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/moxy-androidx-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..bbd3e021 --- /dev/null +++ b/moxy-androidx-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/moxy-androidx-sample/src/main/res/mipmap-hdpi/ic_launcher.png b/moxy-androidx-sample/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..a2f59082 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/moxy-androidx-sample/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..1b523998 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-mdpi/ic_launcher.png b/moxy-androidx-sample/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..ff10afd6 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/moxy-androidx-sample/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..115a4c76 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/moxy-androidx-sample/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..dcd3cd80 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/moxy-androidx-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..459ca609 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/moxy-androidx-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..8ca12fe0 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/moxy-androidx-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..8e19b410 Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/moxy-androidx-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..b824ebdd Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/moxy-androidx-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/moxy-androidx-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..4c19a13c Binary files /dev/null and b/moxy-androidx-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/moxy-androidx-sample/src/main/res/values/colors.xml b/moxy-androidx-sample/src/main/res/values/colors.xml new file mode 100644 index 00000000..3ab3e9cb --- /dev/null +++ b/moxy-androidx-sample/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/moxy-androidx-sample/src/main/res/values/strings.xml b/moxy-androidx-sample/src/main/res/values/strings.xml new file mode 100644 index 00000000..3de299df --- /dev/null +++ b/moxy-androidx-sample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Moxy androidX + diff --git a/moxy-androidx-sample/src/main/res/values/styles.xml b/moxy-androidx-sample/src/main/res/values/styles.xml new file mode 100644 index 00000000..5885930d --- /dev/null +++ b/moxy-androidx-sample/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/moxy-androidx/build.gradle b/moxy-androidx/build.gradle new file mode 100755 index 00000000..67c8e98b --- /dev/null +++ b/moxy-androidx/build.gradle @@ -0,0 +1,235 @@ +apply plugin: 'java' +apply plugin: 'maven-publish' +apply plugin: 'maven' + +ext { + publishDir = 'artifacts' + publishMavenDir = 'artifacts-maven' + targetGroupId = 'tech.schoolhelper' + targetArtefactId = 'moxy-x-androidx' + targetArtefactIdDev = 'moxy-androidx-dev' + versionFile = 'VERSION' +} +configurations { + javadocDeps +} + +// This is important even if Android Studio claims it isn't +// used. Android can't interpret Java 8 byte code. +sourceCompatibility = JavaVersion.VERSION_1_7 +targetCompatibility = JavaVersion.VERSION_1_7 + +def logger = new com.android.build.gradle.internal.LoggerWrapper(project.logger) +def sdkHandler = new com.android.build.gradle.internal.SdkHandler(project, logger) +for (File file : sdkHandler.sdkLoader.repositories) { + project.repositories.maven { + url = file.toURI() + } +} + +task clearPublishDir(type: Delete) { + delete publishDir +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task makeJavadocs(type: Javadoc) { + classpath += configurations.javadocDeps + source = sourceSets.main.allJava +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +task makeJar(type: Jar, dependsOn: classes) { + from('build/classes/java/main') +} + +task copyVersion { + doLast { + def versionFile = new File(project.rootDir, versionFile) + versionFile.write(targetVersionName) + + [publishDir, publishMavenDir].each { dest -> + copy { + from versionFile + into dest + } + } + } +} + +def getBasePom(String libName) { + + def basePom = pom { + //noinspection GroovyAssignabilityCheck + project { + groupId targetGroupId + artifactId targetArtefactId + version targetVersionName + name 'Moxy-androidx' + modelVersion = '4.0.0' + description = POM_DESCRIPTION + url = URL + packaging = 'jar' + + inceptionYear '2016' + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution 'repo' + } + } + + developers { + developer { + name 'Vova Stelmashchuk' + email 'vovochkastelmashchuk@gmail.com' + organization = 'SchoolHelper' + organizationUrl URL + } + } + + scm { + connection POM_SCM_CONNECTION + developerConnection POM_SCM_CONNECTION + url POM_URL + } + }.withXml { + asNode().appendNode('packaging', 'jar') + } + } + + return basePom.withXml { + asNode().dependencies.'*'.findAll { + it.artifactId.text().contains('moxy') + }.each { + it.groupId*.value = targetGroupId + it.version*.value = targetVersionName + } + } +} + +task copyJars { + doLast { + def buildDir = 'build/libs/' + copy { + from buildDir + into publishMavenDir + } + + copy { + from(buildDir) + { + rename targetArtefactId + '(.*)', targetArtefactIdDev + '$1' + } + into publishDir + } + } +} + + +task createPomXml { + doLast { + // creating local pom + + getBasePom('moxy-dev').withXml { + asNode().appendNode('build').appendNode('plugins').with { + appendNode('plugin').with { + appendNode('groupId', 'org.apache.maven.plugins') + appendNode('artifactId', 'maven-gpg-plugin') + appendNode('version', '1.5') + appendNode('executions').with { + appendNode('execution').with { + appendNode('id', 'sign-artifacts') + appendNode('phase', 'verify') + appendNode('goals').with { + appendNode('goal', 'sign') + } + } + } + appendNode('configuration').appendNode('gpgArguments').appendNode('arg', '--no-tty') + } + } + + asNode().artifactId*.value = targetArtefactIdDev + + asNode().dependencies.'*'.findAll() { + it.version.text() == '4.0.+' && project.configurations.compile.allDependencies.find { dep -> + dep.name == it.artifactId.text() + } + }.each() { + it.version*.value = '[4.0,)' + } + + + }.writeTo(publishDir + "/" + targetArtefactIdDev + ".pom") + + //creating global pom + + getBasePom('moxy').withXml { + + asNode().appendNode('build') + .appendNode('plugins') + + + }.writeTo(publishMavenDir + "/" + targetArtefactId + ".pom") + + getBasePom('moxy').withXml { + + asNode().appendNode('build') + .appendNode('plugins') + .appendNode('plugin') + .with { + appendNode('groupId', 'org.sonatype.plugins') + appendNode('artifactId', 'nexus-staging-maven-plugin') + appendNode('version', '1.6.4') + appendNode('extensions', 'true') + appendNode('configuration').with { + appendNode('serverId', 'ossrh') + appendNode('nexusUrl', 'https://oss.sonatype.org/') + appendNode('autoReleaseAfterClose', 'false') + } + } + + asNode().appendNode('distributionManagement').with { + appendNode('snapshotRepository') + .with { + appendNode('id', 'ossrh') + appendNode('url', 'https://oss.sonatype.org/content/repositories/snapshots') + } + appendNode('repository') + .with { + appendNode('id', 'ossrh') + appendNode('url', 'https://oss.sonatype.org/service/local/staging/deploy/maven2/') + } + } + + }.writeTo(publishMavenDir + "/pom.xml") + } +} + +compileJava.dependsOn clearPublishDir +copyJars.dependsOn makeJar +copyJars.dependsOn sourcesJar +javadocJar.dependsOn makeJavadocs +copyJars.dependsOn javadocJar +copyVersion.dependsOn copyJars +copyVersion.dependsOn createPomXml +assemble.dependsOn copyVersion + +dependencies { + implementation project(':moxy-x') + + compileOnly deps.android + compileOnly project(':stub-androidx') + + javadocDeps project(':moxy-x') + javadocDeps deps.android + javadocDeps project(':stub-androidx') +} \ No newline at end of file diff --git a/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java b/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java new file mode 100755 index 00000000..f28fa328 --- /dev/null +++ b/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatActivity.java @@ -0,0 +1,73 @@ +package com.arellomobile.mvp; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; + +/** + * Date: 25-July-18 + * Time: 2:51 + * + * @author Vova Stelmashchuk + */ +@SuppressWarnings("unused") +public class MvpAppCompatActivity extends AppCompatActivity { + private MvpDelegate mMvpDelegate; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getMvpDelegate().onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + + getMvpDelegate().onAttach(); + } + + @Override + protected void onResume() { + super.onResume(); + + getMvpDelegate().onAttach(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + getMvpDelegate().onSaveInstanceState(outState); + getMvpDelegate().onDetach(); + } + + @Override + protected void onStop() { + super.onStop(); + + getMvpDelegate().onDetach(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + getMvpDelegate().onDestroyView(); + + if (isFinishing()) { + getMvpDelegate().onDestroy(); + } + } + + /** + * @return The {@link MvpDelegate} being used by this Activity. + */ + public MvpDelegate getMvpDelegate() { + if (mMvpDelegate == null) { + mMvpDelegate = new MvpDelegate<>(this); + } + return mMvpDelegate; + } +} diff --git a/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatDialogFragment.java b/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatDialogFragment.java new file mode 100644 index 00000000..057bdd2b --- /dev/null +++ b/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatDialogFragment.java @@ -0,0 +1,101 @@ +package com.arellomobile.mvp; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatDialogFragment; +import androidx.fragment.app.Fragment; + +/** + * Date: 15-Feb-19 + * + * @author Vova Usachov + */ +@SuppressWarnings({"unused"}) +public class MvpAppCompatDialogFragment extends AppCompatDialogFragment { + + private boolean mIsStateSaved; + + private MvpDelegate mMvpDelegate; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getMvpDelegate().onCreate(savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + + mIsStateSaved = false; + + getMvpDelegate().onAttach(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + mIsStateSaved = true; + + getMvpDelegate().onSaveInstanceState(outState); + getMvpDelegate().onDetach(); + } + + @Override + public void onStop() { + super.onStop(); + + getMvpDelegate().onDetach(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + getMvpDelegate().onDetach(); + getMvpDelegate().onDestroyView(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + //We leave the screen and respectively all fragments will be destroyed + if (getActivity().isFinishing()) { + getMvpDelegate().onDestroy(); + return; + } + + // When we rotate device isRemoving() return true for fragment placed in backstack + // http://stackoverflow.com/questions/34649126/fragment-back-stack-and-isremoving + if (mIsStateSaved) { + mIsStateSaved = false; + return; + } + + // See https://github.com/Arello-Mobile/Moxy/issues/24 + boolean anyParentIsRemoving = false; + Fragment parent = getParentFragment(); + while (!anyParentIsRemoving && parent != null) { + anyParentIsRemoving = parent.isRemoving(); + parent = parent.getParentFragment(); + } + + if (isRemoving() || anyParentIsRemoving) { + getMvpDelegate().onDestroy(); + } + } + + /** + * @return The {@link MvpDelegate} being used by this Fragment. + */ + public MvpDelegate getMvpDelegate() { + if (mMvpDelegate == null) { + mMvpDelegate = new MvpDelegate<>(this); + } + + return mMvpDelegate; + } +} diff --git a/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java b/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java new file mode 100644 index 00000000..b87f9914 --- /dev/null +++ b/moxy-androidx/src/main/java/com/arellomobile/mvp/MvpAppCompatFragment.java @@ -0,0 +1,109 @@ +package com.arellomobile.mvp; + +import android.os.Bundle; + +import androidx.fragment.app.Fragment; + +/** + * Date: 25-July-18 + * Time: 4:43 + * + * @author Vova Stelmashchuk + */ + +@SuppressWarnings({"ConstantConditions", "unused"}) +public class MvpAppCompatFragment extends Fragment { + + private boolean mIsStateSaved; + + private MvpDelegate mMvpDelegate; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getMvpDelegate().onCreate(savedInstanceState); + } + + @Override + public void onStart() { + super.onStart(); + + mIsStateSaved = false; + + getMvpDelegate().onAttach(); + } + + public void onResume() { + super.onResume(); + + mIsStateSaved = false; + + getMvpDelegate().onAttach(); + } + + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + mIsStateSaved = true; + + getMvpDelegate().onSaveInstanceState(outState); + getMvpDelegate().onDetach(); + } + + @Override + public void onStop() { + super.onStop(); + + getMvpDelegate().onDetach(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + getMvpDelegate().onDetach(); + getMvpDelegate().onDestroyView(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + //We leave the screen and respectively all fragments will be destroyed + if (getActivity().isFinishing()) { + getMvpDelegate().onDestroy(); + return; + } + + // When we rotate device isRemoving() return true for fragment placed in backstack + // http://stackoverflow.com/questions/34649126/fragment-back-stack-and-isremoving + if (mIsStateSaved) { + mIsStateSaved = false; + return; + } + + // See https://github.com/Arello-Mobile/Moxy/issues/24 + boolean anyParentIsRemoving = false; + Fragment parent = getParentFragment(); + while (!anyParentIsRemoving && parent != null) { + anyParentIsRemoving = parent.isRemoving(); + parent = parent.getParentFragment(); + } + + if (isRemoving() || anyParentIsRemoving) { + getMvpDelegate().onDestroy(); + } + } + + /** + * @return The {@link MvpDelegate} being used by this Fragment. + */ + public MvpDelegate getMvpDelegate() { + if (mMvpDelegate == null) { + mMvpDelegate = new MvpDelegate<>(this); + } + + return mMvpDelegate; + } +} \ No newline at end of file diff --git a/moxy-androidx/stub-androidx/build.gradle b/moxy-androidx/stub-androidx/build.gradle new file mode 100755 index 00000000..d6d06d13 --- /dev/null +++ b/moxy-androidx/stub-androidx/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'java' + +sourceCompatibility = JavaVersion.VERSION_1_7 +targetCompatibility = JavaVersion.VERSION_1_7 + +dependencies { + compileOnly deps.android +} diff --git a/moxy-androidx/stub-androidx/src/main/java/androidx/appcompat/app/AppCompatActivity.java b/moxy-androidx/stub-androidx/src/main/java/androidx/appcompat/app/AppCompatActivity.java new file mode 100755 index 00000000..abc8929a --- /dev/null +++ b/moxy-androidx/stub-androidx/src/main/java/androidx/appcompat/app/AppCompatActivity.java @@ -0,0 +1,41 @@ +package androidx.appcompat.app; + +import android.os.Bundle; + +/** + * Date: 25-July-18 + * Time: 2:51 + * + * @author Vova Stelmashchuk + */ + +public class AppCompatActivity { + + protected void onCreate(Bundle savedInstanceState) { + throw new RuntimeException("Stub!"); + } + + protected void onStart() { + throw new RuntimeException("Stub!"); + } + + protected void onResume() { + throw new RuntimeException("Stub!"); + } + + protected void onSaveInstanceState(Bundle outState) { + throw new RuntimeException("Stub!"); + } + + protected void onStop() { + throw new RuntimeException("Stub!"); + } + + protected void onDestroy() { + throw new RuntimeException("Stub!"); + } + + public boolean isFinishing() { + throw new RuntimeException("Stub!"); + } +} diff --git a/moxy-androidx/stub-androidx/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java b/moxy-androidx/stub-androidx/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java new file mode 100644 index 00000000..bc571788 --- /dev/null +++ b/moxy-androidx/stub-androidx/src/main/java/androidx/appcompat/app/AppCompatDialogFragment.java @@ -0,0 +1,46 @@ +package androidx.appcompat.app; + +import android.os.Bundle; + + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + +public class AppCompatDialogFragment { + public void onCreate(Bundle savedInstanceState) { + throw new RuntimeException("Stub!"); + } + + public void onResume() { + throw new RuntimeException("Stub!"); + } + + public void onSaveInstanceState(Bundle outState) { + throw new RuntimeException("Stub!"); + } + + public void onStop() { + throw new RuntimeException("Stub!"); + } + + public void onDestroyView() { + throw new RuntimeException("Stub!"); + } + + public void onDestroy() { + throw new RuntimeException("Stub!"); + } + + final public boolean isRemoving() { + throw new RuntimeException("Stub!"); + } + + public final FragmentActivity getActivity() { + throw new RuntimeException("Stub!"); + } + + public Fragment getParentFragment() { + throw new RuntimeException("Stub!"); + } + +} diff --git a/moxy-androidx/stub-androidx/src/main/java/androidx/fragment/app/Fragment.java b/moxy-androidx/stub-androidx/src/main/java/androidx/fragment/app/Fragment.java new file mode 100644 index 00000000..3c396732 --- /dev/null +++ b/moxy-androidx/stub-androidx/src/main/java/androidx/fragment/app/Fragment.java @@ -0,0 +1,52 @@ +package androidx.fragment.app; + +import android.os.Bundle; + +/** + * Date: 25-July-18 + * Time: 4:38 + * + * @author Vova Stelmashchuk + */ + +public class Fragment { + public void onCreate(Bundle savedInstanceState) { + throw new RuntimeException("Stub!"); + } + + public void onStart() { + throw new RuntimeException("Stub!"); + } + + public void onResume() { + throw new RuntimeException("Stub!"); + } + + public void onSaveInstanceState(Bundle outState) { + throw new RuntimeException("Stub!"); + } + + public void onStop() { + throw new RuntimeException("Stub!"); + } + + public void onDestroyView() { + throw new RuntimeException("Stub!"); + } + + public void onDestroy() { + throw new RuntimeException("Stub!"); + } + + final public boolean isRemoving() { + throw new RuntimeException("Stub!"); + } + + public final FragmentActivity getActivity() { + throw new RuntimeException("Stub!"); + } + + public Fragment getParentFragment() { + throw new RuntimeException("Stub!"); + } +} diff --git a/moxy-androidx/stub-androidx/src/main/java/androidx/fragment/app/FragmentActivity.java b/moxy-androidx/stub-androidx/src/main/java/androidx/fragment/app/FragmentActivity.java new file mode 100644 index 00000000..ff2e0765 --- /dev/null +++ b/moxy-androidx/stub-androidx/src/main/java/androidx/fragment/app/FragmentActivity.java @@ -0,0 +1,14 @@ +package androidx.fragment.app; + +/** + * Date: 25-July-18 + * Time: 4:41 + * + * @author Vova Stelmashchuk + */ + +public class FragmentActivity { + public boolean isFinishing() { + throw new RuntimeException("Stub!"); + } +} diff --git a/moxy-app-compat/build.gradle b/moxy-app-compat/build.gradle index 4fba7312..a9018770 100644 --- a/moxy-app-compat/build.gradle +++ b/moxy-app-compat/build.gradle @@ -5,9 +5,9 @@ apply plugin: 'maven' ext { publishDir = 'artifacts' publishMavenDir = 'artifacts-maven' - targetGroupId = 'com.arello-mobile' - targetArtefactId = 'moxy-app-compat' - targetArtefactIdDev = 'moxy-app-compat-dev' + targetGroupId = 'tech.schoolhelper' + targetArtefactId = 'moxy-x-app-compat' + targetArtefactIdDev = 'moxy-x-app-compat-dev' versionFile = 'VERSION' } configurations { @@ -73,15 +73,15 @@ def getBasePom(String libName) { version targetVersionName name 'Moxy-app-compat' modelVersion = '4.0.0' - description = 'Arello-Mobile MVP library for Android' - url = 'https://www.arello-mobile.com/' + description = 'POM_DESCRIPTION' + url = 'URL' packaging = 'jar' inceptionYear '2016' licenses { license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + name 'POM_LICENCE_NAME' + url 'POM_LICENCE_URL' distribution 'repo' } } @@ -99,12 +99,18 @@ def getBasePom(String libName) { organization = 'Arello Mobile' organizationUrl 'http://www.arello-mobile.com/' } + developer { + name 'Vova Stelmashchuk' + email 'vovochkastelmashchuk@gmail.com' + organization = 'SchoolHelper' + organizationUrl 'https://schoolhelper.tech/' + } } scm { - connection 'scm:git@github.com:Arello-Mobile/Moxy.git' - developerConnection 'scm:git@github.com:Arello-Mobile/Moxy.git' - url 'https://github.com/Arello-Mobile/Moxy' + connection 'POM_SCM_CONNECTION' + developerConnection 'POM_SCM_CONNECTION' + url 'POM_URL' } }.withXml { asNode().appendNode('packaging', 'jar') @@ -140,8 +146,6 @@ task copyJars { } - - task createPomXml { doLast { // creating local pom @@ -233,12 +237,12 @@ copyVersion.dependsOn createPomXml assemble.dependsOn copyVersion dependencies { - compile project(':moxy') + compileOnly project(':moxy-x') compileOnly deps.android compileOnly project(':stub-appcompat') - javadocDeps project(':moxy') + javadocDeps project(':moxy-x') javadocDeps deps.android javadocDeps project(':stub-appcompat') } \ No newline at end of file diff --git a/moxy-app-compat/stub-appcompat/build.gradle b/moxy-app-compat/stub-appcompat/build.gradle index d6d06d13..b54f13db 100644 --- a/moxy-app-compat/stub-appcompat/build.gradle +++ b/moxy-app-compat/stub-appcompat/build.gradle @@ -5,4 +5,4 @@ targetCompatibility = JavaVersion.VERSION_1_7 dependencies { compileOnly deps.android -} +} \ No newline at end of file diff --git a/moxy-compiler/build.gradle b/moxy-compiler/build.gradle index 7a34e47f..9e293a76 100644 --- a/moxy-compiler/build.gradle +++ b/moxy-compiler/build.gradle @@ -5,9 +5,9 @@ apply plugin: 'maven' ext { publishDir = 'artifacts' publishMavenDir = 'artifacts-maven' - targetGroupId = 'com.arello-mobile' - targetArtefactId = 'moxy-compiler' - targetArtefactIdDev = 'moxy-compiler-dev' + targetGroupId = 'tech.schoolhelper' + targetArtefactId = 'moxy-x-compiler' + targetArtefactIdDev = 'moxy-x-compiler-dev' versionFile = 'VERSION' } configurations { @@ -61,17 +61,17 @@ def getBasePom(String libName) { groupId targetGroupId artifactId targetArtefactId version targetVersionName - name 'Moxy-compiler' + name 'Moxy' modelVersion = '4.0.0' - description = 'Arello-Mobile MVP library for Android' - url = 'https://www.arello-mobile.com/' - packaging = 'jar' + description = POM_DESCRIPTION + url = URL + // packaging = 'jar' //doesn't work from box, implement it manualy inceptionYear '2016' licenses { license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + name POM_LICENCE_NAME + url POM_LICENCE_URL distribution 'repo' } } @@ -89,12 +89,18 @@ def getBasePom(String libName) { organization = 'Arello Mobile' organizationUrl 'http://www.arello-mobile.com/' } + developer { + name 'Vova Stelmashchuk' + email 'vovochkastelmashchuk@gmail.com' + organization = 'SchoolHelper' + organizationUrl URL + } } scm { - connection 'scm:git@github.com:Arello-Mobile/Moxy.git' - developerConnection 'scm:git@github.com:Arello-Mobile/Moxy.git' - url 'https://github.com/Arello-Mobile/Moxy' + connection POM_SCM_CONNECTION + developerConnection POM_SCM_CONNECTION + url POM_URL } }.withXml { asNode().appendNode('packaging', 'jar') @@ -247,14 +253,14 @@ copyVersion.dependsOn createPomXml assemble.dependsOn copyVersion dependencies { - implementation project(':moxy') + implementation project(':moxy-x') implementation deps.javapoet compileOnly deps.autocommon compileOnly deps.autoservice compileOnly deps.guava - javadocDeps project(':moxy') + javadocDeps project(':moxy-x') javadocDeps deps.javapoet javadocDeps deps.autoservice @@ -263,6 +269,6 @@ dependencies { testImplementation deps.compiletesting testImplementation deps.asm - // workaround to use test resources (https://stackoverflow.com/q/24870464) + //workaround to use test resources (https://stackoverflow.com/q/24870464) testRuntime files(sourceSets.test.output.resourcesDir) } \ No newline at end of file diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java index 4c6f88d1..507533d2 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java @@ -1,8 +1,7 @@ package com.arellomobile.mvp.compiler; -import com.arellomobile.mvp.GenerateViewState; import com.arellomobile.mvp.InjectViewState; -import com.arellomobile.mvp.RegisterMoxyReflectorPackages; +import com.arellomobile.mvp.RegisterMoxyReflectorDelegate; import com.arellomobile.mvp.compiler.presenterbinder.InjectPresenterProcessor; import com.arellomobile.mvp.compiler.presenterbinder.PresenterBinderClassGenerator; import com.arellomobile.mvp.compiler.reflector.MoxyReflectorGenerator; @@ -16,10 +15,8 @@ import java.io.IOException; import java.lang.annotation.Annotation; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -49,9 +46,6 @@ @SuppressWarnings("unused") @AutoService(Processor.class) public class MvpCompiler extends AbstractProcessor { - public static final String MOXY_REFLECTOR_DEFAULT_PACKAGE = "com.arellomobile.mvp"; - - private static final String OPTION_MOXY_REFLECTOR_PACKAGE = "moxyReflectorPackage"; private static Messager sMessager; private static Types sTypeUtils; @@ -80,19 +74,15 @@ public synchronized void init(ProcessingEnvironment processingEnv) { sOptions = processingEnv.getOptions(); } - @Override - public Set getSupportedOptions() { - return Collections.singleton(OPTION_MOXY_REFLECTOR_PACKAGE); - } - @Override public Set getSupportedAnnotationTypes() { Set supportedAnnotationTypes = new HashSet<>(); - Collections.addAll(supportedAnnotationTypes, + Collections.addAll( + supportedAnnotationTypes, InjectPresenter.class.getCanonicalName(), InjectViewState.class.getCanonicalName(), - RegisterMoxyReflectorPackages.class.getCanonicalName(), - GenerateViewState.class.getCanonicalName()); + RegisterMoxyReflectorDelegate.class.getCanonicalName() + ); return supportedAnnotationTypes; } @@ -140,43 +130,38 @@ private boolean throwableProcess(RoundEnvironment roundEnv) { viewInterfaceProcessor, viewStateClassGenerator); } - String moxyReflectorPackage = sOptions.get(OPTION_MOXY_REFLECTOR_PACKAGE); + String moxyReflectorDelegatePackage = getMoxyReflectorDelegatePackage(roundEnv); - if (moxyReflectorPackage == null) { - moxyReflectorPackage = MOXY_REFLECTOR_DEFAULT_PACKAGE; - } - - List additionalMoxyReflectorPackages = getAdditionalMoxyReflectorPackages(roundEnv); - - JavaFile moxyReflector = MoxyReflectorGenerator.generate( - moxyReflectorPackage, + JavaFile moxyReflectorDelegate = MoxyReflectorGenerator.generate( + moxyReflectorDelegatePackage, injectViewStateProcessor.getPresenterClassNames(), injectPresenterProcessor.getPresentersContainers(), - viewInterfaceProcessor.getUsedStrategies(), - additionalMoxyReflectorPackages + viewInterfaceProcessor.getUsedStrategies() ); - createSourceFile(moxyReflector); + createSourceFile(moxyReflectorDelegate); return true; } - private List getAdditionalMoxyReflectorPackages(RoundEnvironment roundEnv) { - List result = new ArrayList<>(); - - for (Element element : roundEnv.getElementsAnnotatedWith(RegisterMoxyReflectorPackages.class)) { - if (element.getKind() != ElementKind.CLASS) { - getMessager().printMessage(Diagnostic.Kind.ERROR, element + " must be " + ElementKind.CLASS.name() + ", or not mark it as @" + RegisterMoxyReflectorPackages.class.getSimpleName()); - } - - String[] packages = element.getAnnotation(RegisterMoxyReflectorPackages.class).value(); - - Collections.addAll(result, packages); - } - - return result; - } - + private String getMoxyReflectorDelegatePackage( + final RoundEnvironment roundEnv + ) { + final Set elements = roundEnv.getElementsAnnotatedWith(RegisterMoxyReflectorDelegate.class); + + if (elements.isEmpty()) { + getMessager().printMessage( + Diagnostic.Kind.ERROR, + "Cannot find any class annotated with " + RegisterMoxyReflectorDelegate.class.getSimpleName() + " in this module" + ); + } + + return processingEnv + .getElementUtils() + .getPackageOf(elements.iterator().next()) + .getQualifiedName() + .toString(); + } private void checkInjectors(final RoundEnvironment roundEnv, Class clazz, AnnotationRule annotationRule) { for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(clazz)) { diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/InjectPresenterProcessor.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/InjectPresenterProcessor.java index faac2e0e..136914a8 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/InjectPresenterProcessor.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/InjectPresenterProcessor.java @@ -19,183 +19,175 @@ import javax.lang.model.type.TypeMirror; public class InjectPresenterProcessor extends ElementProcessor { - private static final String PRESENTER_FIELD_ANNOTATION = InjectPresenter.class.getName(); - private static final String PROVIDE_PRESENTER_ANNOTATION = ProvidePresenter.class.getName(); - private static final String PROVIDE_PRESENTER_TAG_ANNOTATION = ProvidePresenterTag.class.getName(); + private static final String PRESENTER_FIELD_ANNOTATION = InjectPresenter.class.getName(); - private final List presentersContainers = new ArrayList<>(); + private static final String PROVIDE_PRESENTER_ANNOTATION = ProvidePresenter.class.getName(); - public List getPresentersContainers() { - return new ArrayList<>(presentersContainers); - } + private static final String PROVIDE_PRESENTER_TAG_ANNOTATION = ProvidePresenterTag.class.getName(); - @Override - public TargetClassInfo process(VariableElement variableElement) { - final Element enclosingElement = variableElement.getEnclosingElement(); + private final List presentersContainers = new ArrayList<>(); - if (!(enclosingElement instanceof TypeElement)) { - throw new RuntimeException("Only class fields could be annotated as @InjectPresenter: " + - variableElement + " at " + enclosingElement); - } + public List getPresentersContainers() { + return new ArrayList<>(presentersContainers); + } - if (presentersContainers.contains(enclosingElement)) { - return null; - } + @Override + public TargetClassInfo process(VariableElement variableElement) { + final Element enclosingElement = variableElement.getEnclosingElement(); - final TypeElement presentersContainer = (TypeElement) enclosingElement; - presentersContainers.add(presentersContainer); + if (!(enclosingElement instanceof TypeElement)) { + throw new RuntimeException("Only class fields could be annotated as @InjectPresenter: " + + variableElement + " at " + enclosingElement); + } - // gather presenter fields info - List fields = collectFields(presentersContainer); - bindProvidersToFields(fields, collectPresenterProviders(presentersContainer)); - bindTagProvidersToFields(fields, collectTagProviders(presentersContainer)); + if (presentersContainers.contains(enclosingElement)) { + return null; + } - return new TargetClassInfo(presentersContainer, fields); - } + final TypeElement presentersContainer = (TypeElement) enclosingElement; + presentersContainers.add(presentersContainer); - private static List collectFields(TypeElement presentersContainer) { - List fields = new ArrayList<>(); + // gather presenter fields info + List fields = collectFields(presentersContainer); + bindProvidersToFields(fields, collectPresenterProviders(presentersContainer)); + bindTagProvidersToFields(fields, collectTagProviders(presentersContainer)); - for (Element element : presentersContainer.getEnclosedElements()) { - if (element.getKind() != ElementKind.FIELD) { - continue; - } + return new TargetClassInfo(presentersContainer, fields); + } - AnnotationMirror annotation = Util.getAnnotation(element, PRESENTER_FIELD_ANNOTATION); + private static List collectFields(TypeElement presentersContainer) { + List fields = new ArrayList<>(); - if (annotation == null) { - continue; - } + for (Element element : presentersContainer.getEnclosedElements()) { + if (element.getKind() != ElementKind.FIELD) { + continue; + } - // TODO: simplify? - TypeMirror clazz = ((DeclaredType) element.asType()).asElement().asType(); + AnnotationMirror annotation = Util.getAnnotation(element, PRESENTER_FIELD_ANNOTATION); - String name = element.toString(); + if (annotation == null) { + continue; + } - String type = Util.getAnnotationValueAsString(annotation, "type"); - String tag = Util.getAnnotationValueAsString(annotation, "tag"); - String presenterId = Util.getAnnotationValueAsString(annotation, "presenterId"); + // TODO: simplify? + TypeMirror clazz = ((DeclaredType) element.asType()).asElement().asType(); - TargetPresenterField field = new TargetPresenterField(clazz, name, type, tag, presenterId); - fields.add(field); - } - return fields; - } + String name = element.toString(); - private static List collectPresenterProviders(TypeElement presentersContainer) { - List providers = new ArrayList<>(); + String tag = Util.getAnnotationValueAsString(annotation, "tag"); + String presenterId = Util.getAnnotationValueAsString(annotation, "presenterId"); - for (Element element : presentersContainer.getEnclosedElements()) { - if (element.getKind() != ElementKind.METHOD) { - continue; - } + TargetPresenterField field = new TargetPresenterField(clazz, name, tag, presenterId); + fields.add(field); + } + return fields; + } - final ExecutableElement providerMethod = (ExecutableElement) element; - - final AnnotationMirror annotation = Util.getAnnotation(element, PROVIDE_PRESENTER_ANNOTATION); + private static List collectPresenterProviders(TypeElement presentersContainer) { + List providers = new ArrayList<>(); - if (annotation == null) { - continue; - } - - final String name = providerMethod.getSimpleName().toString(); - final DeclaredType kind = ((DeclaredType) providerMethod.getReturnType()); - - String type = Util.getAnnotationValueAsString(annotation, "type"); - String tag = Util.getAnnotationValueAsString(annotation, "tag"); - String presenterId = Util.getAnnotationValueAsString(annotation, "presenterId"); - - providers.add(new PresenterProviderMethod(kind, name, type, tag, presenterId)); - } - return providers; - } - - private static List collectTagProviders(TypeElement presentersContainer) { - List providers = new ArrayList<>(); - - for (Element element : presentersContainer.getEnclosedElements()) { - if (element.getKind() != ElementKind.METHOD) { - continue; - } - - final ExecutableElement providerMethod = (ExecutableElement) element; - - final AnnotationMirror annotation = Util.getAnnotation(element, PROVIDE_PRESENTER_TAG_ANNOTATION); - - if (annotation == null) { - continue; - } - - final String name = providerMethod.getSimpleName().toString(); - - TypeMirror presenterClass = Util.getAnnotationValueAsTypeMirror(annotation, "presenterClass"); - String type = Util.getAnnotationValueAsString(annotation, "type"); - String presenterId = Util.getAnnotationValueAsString(annotation, "presenterId"); - - providers.add(new TagProviderMethod(presenterClass, name, type, presenterId)); - } - return providers; - } - - private static void bindProvidersToFields(List fields, - List presenterProviders) { - if (fields.isEmpty() || presenterProviders.isEmpty()) { - return; - } - - for (PresenterProviderMethod presenterProvider : presenterProviders) { - TypeMirror providerTypeMirror = presenterProvider.getClazz().asElement().asType(); - - for (TargetPresenterField field : fields) { - if ((field.getClazz()).equals(providerTypeMirror)) { - if (field.getPresenterType() != presenterProvider.getPresenterType()) { - continue; - } - - if (field.getTag() == null && presenterProvider.getTag() != null) { - continue; - } - if (field.getTag() != null && !field.getTag().equals(presenterProvider.getTag())) { - continue; - } - - if (field.getPresenterId() == null && presenterProvider.getPresenterId() != null) { - continue; - } - if (field.getPresenterId() != null && !field.getPresenterId().equals(presenterProvider.getPresenterId())) { - continue; - } - - field.setPresenterProviderMethodName(presenterProvider.getName()); - } - } - - } - } - - private static void bindTagProvidersToFields(List fields, - List tagProviders) { - if (fields.isEmpty() || tagProviders.isEmpty()) { - return; - } - - for (TagProviderMethod tagProvider : tagProviders) { - for (TargetPresenterField field : fields) { - if ((field.getClazz()).equals(tagProvider.getPresenterClass())) { - if (field.getPresenterType() != tagProvider.getType()) { - continue; - } - - if (field.getPresenterId() == null && tagProvider.getPresenterId() != null) { - continue; - } - if (field.getPresenterId() != null && !field.getPresenterId().equals(tagProvider.getPresenterId())) { - continue; - } - - field.setPresenterTagProviderMethodName(tagProvider.getMethodName()); - } - } - } - } + for (Element element : presentersContainer.getEnclosedElements()) { + if (element.getKind() != ElementKind.METHOD) { + continue; + } + + final ExecutableElement providerMethod = (ExecutableElement) element; + + final AnnotationMirror annotation = Util.getAnnotation(element, PROVIDE_PRESENTER_ANNOTATION); + + if (annotation == null) { + continue; + } + + final String name = providerMethod.getSimpleName().toString(); + final DeclaredType kind = ((DeclaredType) providerMethod.getReturnType()); + + String tag = Util.getAnnotationValueAsString(annotation, "tag"); + String presenterId = Util.getAnnotationValueAsString(annotation, "presenterId"); + + providers.add(new PresenterProviderMethod(kind, name, tag, presenterId)); + } + return providers; + } + + private static List collectTagProviders(TypeElement presentersContainer) { + List providers = new ArrayList<>(); + + for (Element element : presentersContainer.getEnclosedElements()) { + if (element.getKind() != ElementKind.METHOD) { + continue; + } + + final ExecutableElement providerMethod = (ExecutableElement) element; + + final AnnotationMirror annotation = Util.getAnnotation(element, PROVIDE_PRESENTER_TAG_ANNOTATION); + + if (annotation == null) { + continue; + } + + final String name = providerMethod.getSimpleName().toString(); + + TypeMirror presenterClass = Util.getAnnotationValueAsTypeMirror(annotation, "presenterClass"); + String type = Util.getAnnotationValueAsString(annotation, "type"); + String presenterId = Util.getAnnotationValueAsString(annotation, "presenterId"); + + providers.add(new TagProviderMethod(presenterClass, name, type, presenterId)); + } + return providers; + } + + private static void bindProvidersToFields(List fields, + List presenterProviders) { + if (fields.isEmpty() || presenterProviders.isEmpty()) { + return; + } + + for (PresenterProviderMethod presenterProvider : presenterProviders) { + TypeMirror providerTypeMirror = presenterProvider.getClazz().asElement().asType(); + + for (TargetPresenterField field : fields) { + if ((field.getClazz()).equals(providerTypeMirror)) { + if (field.getTag() == null && presenterProvider.getTag() != null) { + continue; + } + if (field.getTag() != null && !field.getTag().equals(presenterProvider.getTag())) { + continue; + } + + if (field.getPresenterId() == null && presenterProvider.getPresenterId() != null) { + continue; + } + if (field.getPresenterId() != null && !field.getPresenterId().equals(presenterProvider.getPresenterId())) { + continue; + } + + field.setPresenterProviderMethodName(presenterProvider.getName()); + } + } + + } + } + + private static void bindTagProvidersToFields(List fields, + List tagProviders) { + if (fields.isEmpty() || tagProviders.isEmpty()) { + return; + } + + for (TagProviderMethod tagProvider : tagProviders) { + for (TargetPresenterField field : fields) { + if ((field.getClazz()).equals(tagProvider.getPresenterClass())) { + if (field.getPresenterId() == null && tagProvider.getPresenterId() != null) { + continue; + } + if (field.getPresenterId() != null && !field.getPresenterId().equals(tagProvider.getPresenterId())) { + continue; + } + + field.setPresenterTagProviderMethodName(tagProvider.getMethodName()); + } + } + } + } } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterBinderClassGenerator.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterBinderClassGenerator.java index 1165499e..7de4b91b 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterBinderClassGenerator.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterBinderClassGenerator.java @@ -50,129 +50,128 @@ */ public final class PresenterBinderClassGenerator extends JavaFilesGenerator { - @Override - public List generate(TargetClassInfo targetClassInfo) { - ClassName targetClassName = targetClassInfo.getName(); - List fields = targetClassInfo.getFields(); - - final String containerSimpleName = String.join("$", targetClassName.simpleNames()); - - TypeSpec.Builder classBuilder = TypeSpec.classBuilder(containerSimpleName + MvpProcessor.PRESENTER_BINDER_SUFFIX) - .addModifiers(Modifier.PUBLIC) - .superclass(ParameterizedTypeName.get(ClassName.get(PresenterBinder.class), targetClassName)); - - for (TargetPresenterField field : fields) { - classBuilder.addType(generatePresenterBinderClass(field, targetClassName)); - } - - classBuilder.addMethod(generateGetPresentersMethod(fields, targetClassName)); - - JavaFile javaFile = JavaFile.builder(targetClassName.packageName(), classBuilder.build()) - .indent("\t") - .build(); - return Collections.singletonList(javaFile); - } - - private static MethodSpec generateGetPresentersMethod(List fields, - ClassName containerClassName) { - MethodSpec.Builder builder = MethodSpec.methodBuilder("getPresenterFields") - .addAnnotation(Override.class) - .addModifiers(Modifier.PUBLIC) - .returns(ParameterizedTypeName.get( - ClassName.get(List.class), ParameterizedTypeName.get( - ClassName.get(PresenterField.class), containerClassName))); - - builder.addStatement("$T<$T<$T>> presenters = new $T<>($L)", - List.class, PresenterField.class, containerClassName, - ArrayList.class, fields.size()); - - for (TargetPresenterField field : fields) { - builder.addStatement("presenters.add(new $L())", field.getGeneratedClassName()); - } - - builder.addStatement("return presenters"); - - return builder.build(); - } - - private static TypeSpec generatePresenterBinderClass(TargetPresenterField field, - ClassName targetClassName) { - String tag = field.getTag(); - if (tag == null) tag = field.getName(); - - TypeSpec.Builder classBuilder = TypeSpec.classBuilder(field.getGeneratedClassName()) - .addModifiers(Modifier.PUBLIC) - .superclass(ParameterizedTypeName.get( - ClassName.get(PresenterField.class), targetClassName)) - .addMethod(generatePresenterBinderConstructor(field, tag)) - .addMethod(generateBindMethod(field, targetClassName)) - .addMethod(generateProvidePresenterMethod(field, targetClassName)); - - String tagProviderMethodName = field.getPresenterTagProviderMethodName(); - if (tagProviderMethodName != null) { - classBuilder.addMethod(generateGetTagMethod(tagProviderMethodName, targetClassName)); - } - - return classBuilder.build(); - } - - private static MethodSpec generatePresenterBinderConstructor(TargetPresenterField field, String tag) { - return MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addStatement("super($S, $T.$L, $S, $T.class)", - tag, - field.getPresenterType().getDeclaringClass(), - field.getPresenterType().name(), - field.getPresenterId(), - field.getTypeName()) - .build(); - } - - private static MethodSpec generateBindMethod(TargetPresenterField field, - ClassName targetClassName) { - return MethodSpec.methodBuilder("bind") - .addAnnotation(Override.class) - .addModifiers(Modifier.PUBLIC) - .addParameter(targetClassName, "target") - .addParameter(MvpPresenter.class, "presenter") - .addStatement("target.$L = ($T) presenter", field.getName(), field.getTypeName()) - .build(); - } - - private static MethodSpec generateProvidePresenterMethod(TargetPresenterField field, - ClassName targetClassName) { - MethodSpec.Builder builder = MethodSpec.methodBuilder("providePresenter") - .addAnnotation(Override.class) - .addModifiers(Modifier.PUBLIC) - .returns(ParameterizedTypeName.get( - ClassName.get(MvpPresenter.class), WildcardTypeName.subtypeOf(Object.class))) - .addParameter(targetClassName, "delegated"); - - if (field.getPresenterProviderMethodName() != null) { - builder.addStatement("return delegated.$L()", field.getPresenterProviderMethodName()); - } else { - boolean hasEmptyConstructor = Util.hasEmptyConstructor((TypeElement) ((DeclaredType) field.getClazz()).asElement()); - - if (hasEmptyConstructor) { - builder.addStatement("return new $T()", field.getTypeName()); - } else { - builder.addStatement( - "throw new $T($S + $S)", IllegalStateException.class, - field.getClazz(), " has not default constructor. You can apply @ProvidePresenter to some method which will construct Presenter. Also you can make it default constructor"); - } - } - - return builder.build(); - } - - private static MethodSpec generateGetTagMethod(String tagProviderMethodName, - ClassName targetClassName) { - return MethodSpec.methodBuilder("getTag") - .addAnnotation(Override.class) - .addModifiers(Modifier.PUBLIC) - .returns(String.class) - .addParameter(targetClassName, "delegated") - .addStatement("return String.valueOf(delegated.$L())", tagProviderMethodName) - .build(); - } + @Override + public List generate(TargetClassInfo targetClassInfo) { + ClassName targetClassName = targetClassInfo.getName(); + List fields = targetClassInfo.getFields(); + + final String containerSimpleName = String.join("$", targetClassName.simpleNames()); + + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(containerSimpleName + MvpProcessor.PRESENTER_BINDER_SUFFIX) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(ClassName.get(PresenterBinder.class), targetClassName)); + + for (TargetPresenterField field : fields) { + classBuilder.addType(generatePresenterBinderClass(field, targetClassName)); + } + + classBuilder.addMethod(generateGetPresentersMethod(fields, targetClassName)); + + JavaFile javaFile = JavaFile.builder(targetClassName.packageName(), classBuilder.build()) + .indent("\t") + .build(); + return Collections.singletonList(javaFile); + } + + private static MethodSpec generateGetPresentersMethod(List fields, + ClassName containerClassName) { + MethodSpec.Builder builder = MethodSpec.methodBuilder("getPresenterFields") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get( + ClassName.get(List.class), ParameterizedTypeName.get( + ClassName.get(PresenterField.class), containerClassName))); + + builder.addStatement("$T<$T<$T>> presenters = new $T<>($L)", + List.class, PresenterField.class, containerClassName, + ArrayList.class, fields.size()); + + for (TargetPresenterField field : fields) { + builder.addStatement("presenters.add(new $L())", field.getGeneratedClassName()); + } + + builder.addStatement("return presenters"); + + return builder.build(); + } + + private static TypeSpec generatePresenterBinderClass(TargetPresenterField field, + ClassName targetClassName) { + String tag = field.getTag(); + if (tag == null) { + tag = field.getName(); + } + + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(field.getGeneratedClassName()) + .addModifiers(Modifier.PUBLIC) + .superclass(ParameterizedTypeName.get(ClassName.get(PresenterField.class), targetClassName)) + .addMethod(generatePresenterBinderConstructor(field, tag)) + .addMethod(generateBindMethod(field, targetClassName)) + .addMethod(generateProvidePresenterMethod(field, targetClassName)); + + String tagProviderMethodName = field.getPresenterTagProviderMethodName(); + if (tagProviderMethodName != null) { + classBuilder.addMethod(generateGetTagMethod(tagProviderMethodName, targetClassName)); + } + + return classBuilder.build(); + } + + private static MethodSpec generatePresenterBinderConstructor(TargetPresenterField field, String tag) { + return MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addStatement("super($S, $S, $T.class)", + tag, + field.getPresenterId(), + field.getTypeName()) + .build(); + } + + private static MethodSpec generateBindMethod(TargetPresenterField field, + ClassName targetClassName) { + return MethodSpec.methodBuilder("bind") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .addParameter(targetClassName, "target") + .addParameter(MvpPresenter.class, "presenter") + .addStatement("target.$L = ($T) presenter", field.getName(), field.getTypeName()) + .build(); + } + + private static MethodSpec generateProvidePresenterMethod(TargetPresenterField field, + ClassName targetClassName) { + MethodSpec.Builder builder = MethodSpec.methodBuilder("providePresenter") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get( + ClassName.get(MvpPresenter.class), WildcardTypeName.subtypeOf(Object.class))) + .addParameter(targetClassName, "delegated"); + + if (field.getPresenterProviderMethodName() != null) { + builder.addStatement("return delegated.$L()", field.getPresenterProviderMethodName()); + } else { + boolean hasEmptyConstructor = Util.hasEmptyConstructor((TypeElement) ((DeclaredType) field.getClazz()).asElement()); + + if (hasEmptyConstructor) { + builder.addStatement("return new $T()", field.getTypeName()); + } else { + builder.addStatement( + "throw new $T($S + $S)", IllegalStateException.class, + field.getClazz(), " has not default constructor. You can apply @ProvidePresenter to some method which will construct Presenter. Also you can make it default constructor"); + } + } + + return builder.build(); + } + + private static MethodSpec generateGetTagMethod(String tagProviderMethodName, + ClassName targetClassName) { + return MethodSpec.methodBuilder("getTag") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(String.class) + .addParameter(targetClassName, "delegated") + .addStatement("return String.valueOf(delegated.$L())", tagProviderMethodName) + .build(); + } } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterProviderMethod.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterProviderMethod.java index 43814cc3..643abb18 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterProviderMethod.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/PresenterProviderMethod.java @@ -1,24 +1,16 @@ package com.arellomobile.mvp.compiler.presenterbinder; -import com.arellomobile.mvp.presenter.PresenterType; - import javax.lang.model.type.DeclaredType; class PresenterProviderMethod { private final DeclaredType clazz; private final String name; - private final PresenterType presenterType; private final String tag; private final String presenterId; - PresenterProviderMethod(DeclaredType clazz, String name, String type, String tag, String presenterId) { + PresenterProviderMethod(DeclaredType clazz, String name, String tag, String presenterId) { this.clazz = clazz; this.name = name; - if (type == null) { - presenterType = PresenterType.LOCAL; - } else { - presenterType = PresenterType.valueOf(type); - } this.tag = tag; this.presenterId = presenterId; } @@ -31,10 +23,6 @@ String getName() { return name; } - PresenterType getPresenterType() { - return presenterType; - } - String getTag() { return tag; } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TagProviderMethod.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TagProviderMethod.java index 8e01be05..34207c3d 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TagProviderMethod.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TagProviderMethod.java @@ -1,39 +1,29 @@ package com.arellomobile.mvp.compiler.presenterbinder; -import com.arellomobile.mvp.presenter.PresenterType; - import javax.lang.model.type.TypeMirror; class TagProviderMethod { - private final TypeMirror presenterClass; - private final String methodName; - private final PresenterType type; - private final String presenterId; - - TagProviderMethod(TypeMirror presenterClass, String methodName, String type, String presenterId) { - this.presenterClass = presenterClass; - this.methodName = methodName; - if (type == null) { - this.type = PresenterType.LOCAL; - } else { - this.type = PresenterType.valueOf(type); - } - this.presenterId = presenterId; - } - - TypeMirror getPresenterClass() { - return presenterClass; - } - - String getMethodName() { - return methodName; - } - - PresenterType getType() { - return type; - } - - String getPresenterId() { - return presenterId; - } + private final TypeMirror presenterClass; + + private final String methodName; + + private final String presenterId; + + TagProviderMethod(TypeMirror presenterClass, String methodName, String type, String presenterId) { + this.presenterClass = presenterClass; + this.methodName = methodName; + this.presenterId = presenterId; + } + + TypeMirror getPresenterClass() { + return presenterClass; + } + + String getMethodName() { + return methodName; + } + + String getPresenterId() { + return presenterId; + } } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TargetPresenterField.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TargetPresenterField.java index 02c6b3dc..d1a52266 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TargetPresenterField.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/presenterbinder/TargetPresenterField.java @@ -1,82 +1,77 @@ package com.arellomobile.mvp.compiler.presenterbinder; import com.arellomobile.mvp.MvpProcessor; -import com.arellomobile.mvp.presenter.PresenterType; +import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import javax.lang.model.type.TypeMirror; class TargetPresenterField { - private final TypeMirror clazz; - private final TypeName typeName; - private final String name; - private final PresenterType presenterType; - private final String tag; - private final String presenterId; - - private String presenterProviderMethodName; - private String presenterTagProviderMethodName; - - TargetPresenterField(TypeMirror clazz, - String name, - String presenterType, - String tag, - String presenterId) { - this.clazz = clazz; - this.typeName = TypeName.get(clazz); - this.name = name; - this.tag = tag; - - if (presenterType == null) { - this.presenterType = PresenterType.LOCAL; - } else { - this.presenterType = PresenterType.valueOf(presenterType); - } - - this.presenterId = presenterId; - } - - TypeMirror getClazz() { - return clazz; - } - - TypeName getTypeName() { - return typeName; - } - - String getGeneratedClassName() { - return name + MvpProcessor.PRESENTER_BINDER_INNER_SUFFIX; - } - - String getTag() { - return tag; - } - - String getName() { - return name; - } - - PresenterType getPresenterType() { - return presenterType; - } - - String getPresenterId() { - return presenterId; - } - - String getPresenterProviderMethodName() { - return presenterProviderMethodName; - } - - void setPresenterProviderMethodName(String presenterProviderMethodName) { - this.presenterProviderMethodName = presenterProviderMethodName; - } - - String getPresenterTagProviderMethodName() { - return presenterTagProviderMethodName; - } - - void setPresenterTagProviderMethodName(String presenterTagProviderMethodName) { - this.presenterTagProviderMethodName = presenterTagProviderMethodName; - } + private final TypeMirror clazz; + + private final boolean isParametrized; + + private final TypeName typeName; + + private final String name; + + private final String tag; + + private final String presenterId; + + private String presenterProviderMethodName; + + private String presenterTagProviderMethodName; + + TargetPresenterField(TypeMirror clazz, + String name, + String tag, + String presenterId) { + this.clazz = clazz; + this.isParametrized = TypeName.get(clazz) instanceof ParameterizedTypeName; + this.typeName = isParametrized ? ((ParameterizedTypeName) TypeName.get(clazz)).rawType : TypeName.get(clazz); + this.name = name; + this.tag = tag; + this.presenterId = presenterId; + } + + TypeMirror getClazz() { + return clazz; + } + + TypeName getTypeName() { + return typeName; + } + + String getGeneratedClassName() { + return name + MvpProcessor.PRESENTER_BINDER_INNER_SUFFIX; + } + + String getTag() { + return tag; + } + + String getName() { + return name; + } + + String getPresenterId() { + return presenterId; + } + + String getPresenterProviderMethodName() { + return presenterProviderMethodName; + } + + void setPresenterProviderMethodName(String presenterProviderMethodName) { + this.presenterProviderMethodName = presenterProviderMethodName; + } + + String getPresenterTagProviderMethodName() { + return presenterTagProviderMethodName; + } + + void setPresenterTagProviderMethodName(String presenterTagProviderMethodName) { + this.presenterTagProviderMethodName = presenterTagProviderMethodName; + } } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java index c00dc74e..c11b99c6 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java @@ -2,6 +2,7 @@ import com.arellomobile.mvp.MvpProcessor; import com.arellomobile.mvp.ViewStateProvider; +import com.arellomobile.mvp.reflector.ReflectorDelegate; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; @@ -26,8 +27,6 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import static com.arellomobile.mvp.compiler.MvpCompiler.MOXY_REFLECTOR_DEFAULT_PACKAGE; - /** * Date: 07.12.2016 * Time: 19:05 @@ -50,61 +49,46 @@ public class MoxyReflectorGenerator { public static JavaFile generate(String destinationPackage, List presenterClassNames, List presentersContainers, - List strategyClasses, - List additionalMoxyReflectorsPackages) { - TypeSpec.Builder classBuilder = TypeSpec.classBuilder("MoxyReflector") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + List strategyClasses) { + + TypeSpec.Builder classBuilder = TypeSpec.enumBuilder("MoxyReflectorDelegate") + .addEnumConstant("INSTANCE") + .addModifiers(Modifier.PUBLIC) + .addSuperinterface(TypeName.get(ReflectorDelegate.class)) .addField(MAP_CLASS_TO_OBJECT_TYPE_NAME, "sViewStateProviders", Modifier.PRIVATE, Modifier.STATIC) .addField(MAP_CLASS_TO_LIST_OF_OBJECT_TYPE_NAME, "sPresenterBinders", Modifier.PRIVATE, Modifier.STATIC) .addField(MAP_CLASS_TO_OBJECT_TYPE_NAME, "sStrategies", Modifier.PRIVATE, Modifier.STATIC); - classBuilder.addStaticBlock(generateStaticInitializer(presenterClassNames, presentersContainers, strategyClasses, additionalMoxyReflectorsPackages)); - - if (destinationPackage.equals(MOXY_REFLECTOR_DEFAULT_PACKAGE)) { - classBuilder.addMethod(MethodSpec.methodBuilder("getViewState") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(Object.class) - .addParameter(CLASS_WILDCARD_TYPE_NAME, "presenterClass") - .addStatement("$1T viewStateProvider = ($1T) sViewStateProviders.get(presenterClass)", ViewStateProvider.class) - .beginControlFlow("if (viewStateProvider == null)") - .addStatement("return null") - .endControlFlow() - .addCode("\n") - .addStatement("return viewStateProvider.getViewState()") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(ParameterizedTypeName.get(List.class, Object.class)) - .addParameter(CLASS_WILDCARD_TYPE_NAME, "delegated") - .addStatement("return sPresenterBinders.get(delegated)") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getStrategy") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(Object.class) - .addParameter(CLASS_WILDCARD_TYPE_NAME, "strategyClass") - .addStatement("return sStrategies.get(strategyClass)") - .build()); - } else { - classBuilder.addMethod(MethodSpec.methodBuilder("getViewStateProviders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_OBJECT_TYPE_NAME) - .addStatement("return viewStateProvider.getViewState()") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_LIST_OF_OBJECT_TYPE_NAME) - .addStatement("return sViewStateProviders") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getStrategies") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_OBJECT_TYPE_NAME) - .addStatement("return sStrategies") - .build()); - } + classBuilder.addStaticBlock(generateStaticInitializer(presenterClassNames, presentersContainers, strategyClasses)); + + classBuilder.addMethod(MethodSpec.methodBuilder("getViewState") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(Object.class) + .addParameter(CLASS_WILDCARD_TYPE_NAME, "presenterClass") + .addStatement("$1T viewStateProvider = ($1T) sViewStateProviders.get(presenterClass)", ViewStateProvider.class) + .beginControlFlow("if (viewStateProvider == null)") + .addStatement("return null") + .endControlFlow() + .addCode("\n") + .addStatement("return viewStateProvider.getViewState()") + .build()); + + classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get(List.class, Object.class)) + .addParameter(CLASS_WILDCARD_TYPE_NAME, "delegated") + .addStatement("return sPresenterBinders.get(delegated)") + .build()); + + classBuilder.addMethod(MethodSpec.methodBuilder("getStrategy") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(Object.class) + .addParameter(CLASS_WILDCARD_TYPE_NAME, "strategyClass") + .addStatement("return sStrategies.get(strategyClass)") + .build()); return JavaFile.builder(destinationPackage, classBuilder.build()) @@ -114,13 +98,11 @@ public static JavaFile generate(String destinationPackage, private static CodeBlock generateStaticInitializer(List presenterClassNames, List presentersContainers, - List strategyClasses, - List additionalMoxyReflectorsPackages) { + List strategyClasses) { // sort to preserve order of statements between compilations Map> presenterBinders = getPresenterBinders(presentersContainers); presenterClassNames.sort(TYPE_ELEMENT_COMPARATOR); strategyClasses.sort(TYPE_ELEMENT_COMPARATOR); - additionalMoxyReflectorsPackages.sort(Comparator.naturalOrder()); CodeBlock.Builder builder = CodeBlock.builder(); @@ -161,15 +143,6 @@ private static CodeBlock generateStaticInitializer(List presenterCl builder.addStatement("sStrategies.put($1T.class, new $1T())", strategyClass); } - for (String pkg : additionalMoxyReflectorsPackages) { - ClassName moxyReflector = ClassName.get(pkg, "MoxyReflector"); - - builder.add("\n"); - builder.addStatement("sViewStateProviders.putAll($T.getViewStateProviders())", moxyReflector); - builder.addStatement("sPresenterBinders.putAll($T.getPresenterBinders())", moxyReflector); - builder.addStatement("sStrategies.putAll($T.getStrategies())", moxyReflector); - } - return builder.build(); } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceInfo.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceInfo.java index e81cdf17..d5069fb1 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceInfo.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceInfo.java @@ -1,6 +1,5 @@ package com.arellomobile.mvp.compiler.viewstate; -import com.google.common.collect.Iterables; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; @@ -18,19 +17,25 @@ * @author Evgeny Kursakov */ class ViewInterfaceInfo { + private final TypeElement element; private final ClassName name; private final List typeVariables; private final List methods; - ViewInterfaceInfo(TypeElement name, List methods) { - this.name = ClassName.get(name); + ViewInterfaceInfo(TypeElement element, List methods) { + this.element = element; + this.name = ClassName.get(element); this.methods = methods; - this.typeVariables = name.getTypeParameters().stream() + this.typeVariables = element.getTypeParameters().stream() .map(TypeVariableName::get) .collect(Collectors.toList()); } + public TypeElement getElement() { + return element; + } + ClassName getName() { return name; } @@ -39,7 +44,10 @@ TypeName getNameWithTypeVariables() { if (typeVariables.isEmpty()) { return name; } else { - return ParameterizedTypeName.get(name, Iterables.toArray(typeVariables, TypeVariableName.class)); + TypeVariableName[] names = new TypeVariableName[typeVariables.size()]; + typeVariables.toArray(names); + + return ParameterizedTypeName.get(name, names); } } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceProcessor.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceProcessor.java index 8588ab38..18d8a960 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceProcessor.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewInterfaceProcessor.java @@ -37,7 +37,8 @@ public class ViewInterfaceProcessor extends ElementProcessor usedStrategies = new HashSet<>(); public List getUsedStrategies() { @@ -46,7 +47,8 @@ public List getUsedStrategies() { @Override public ViewInterfaceInfo process(TypeElement element) { - viewClassName = element.getSimpleName().toString(); + this.viewInterfaceElement = element; + viewInterfaceName = element.getSimpleName().toString(); List methods = new ArrayList<>(); @@ -126,7 +128,9 @@ private void getMethods(TypeElement typeElement, // add strategy to list usedStrategies.add(strategyClass); - final ViewMethod method = new ViewMethod(methodElement, strategyClass, methodTag); + final ViewMethod method = new ViewMethod( + (DeclaredType) viewInterfaceElement.asType(), methodElement, strategyClass, methodTag + ); if (rootMethods.contains(method)) { continue; @@ -161,7 +165,7 @@ private void checkStrategyAndTagEquals(ViewMethod method, ViewMethod existingMet " and " + method.getEnclosedClassName() + " has method " + method.getName() + "(" + arguments + ")" + " with different " + parts + "." + - " Override this method in " + viewClassName + " or make " + parts + " equals"); + " Override this method in " + viewInterfaceName + " or make " + parts + " equals"); } } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewMethod.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewMethod.java index 83065fa3..c4446751 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewMethod.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewMethod.java @@ -1,14 +1,21 @@ package com.arellomobile.mvp.compiler.viewstate; +import com.arellomobile.mvp.compiler.MvpCompiler; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeVariableName; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; /** * Date: 27-Jul-2017 @@ -17,7 +24,7 @@ * @author Evgeny Kursakov */ class ViewMethod { - private final ExecutableElement element; + private final ExecutableElement methodElement; private final String name; private final TypeElement strategy; private final String tag; @@ -28,18 +35,32 @@ class ViewMethod { private String uniqueSuffix; - ViewMethod(ExecutableElement methodElement, + ViewMethod(DeclaredType targetInterfaceElement, + ExecutableElement methodElement, TypeElement strategy, String tag) { - this.element = methodElement; + this.methodElement = methodElement; this.name = methodElement.getSimpleName().toString(); this.strategy = strategy; this.tag = tag; - this.parameterSpecs = methodElement.getParameters() - .stream() - .map(ParameterSpec::get) - .collect(Collectors.toList()); + this.parameterSpecs = new ArrayList<>(); + + Types typeUtils = MvpCompiler.getTypeUtils(); + ExecutableType executableType = (ExecutableType) typeUtils.asMemberOf(targetInterfaceElement, methodElement); + List parameters = methodElement.getParameters(); + List resolvedParameterTypes = executableType.getParameterTypes(); + + for (int i = 0; i < parameters.size(); i++) { + VariableElement element = parameters.get(i); + TypeName type = TypeName.get(resolvedParameterTypes.get(i)); + String name = element.getSimpleName().toString(); + + parameterSpecs.add(ParameterSpec.builder(type, name) + .addModifiers(element.getModifiers()) + .build() + ); + } this.exceptions = methodElement.getThrownTypes().stream() .map(TypeName::get) @@ -50,7 +71,7 @@ class ViewMethod { .map(TypeVariableName::get) .collect(Collectors.toList()); - this.argumentsString = parameterSpecs.stream() + this.argumentsString = this.parameterSpecs.stream() .map(parameterSpec -> parameterSpec.name) .collect(Collectors.joining(", ")); @@ -58,7 +79,7 @@ class ViewMethod { } ExecutableElement getElement() { - return element; + return methodElement; } String getName() { @@ -94,7 +115,7 @@ String getCommandClassName() { } String getEnclosedClassName() { - TypeElement typeElement = (TypeElement) element.getEnclosingElement(); + TypeElement typeElement = (TypeElement) methodElement.getEnclosingElement(); return typeElement.getQualifiedName().toString(); } diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewStateClassGenerator.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewStateClassGenerator.java index 266f7b70..a7962568 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewStateClassGenerator.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstate/ViewStateClassGenerator.java @@ -2,6 +2,7 @@ import com.arellomobile.mvp.MvpProcessor; import com.arellomobile.mvp.compiler.JavaFilesGenerator; +import com.arellomobile.mvp.compiler.MvpCompiler; import com.arellomobile.mvp.viewstate.MvpViewState; import com.arellomobile.mvp.viewstate.ViewCommand; import com.squareup.javapoet.ClassName; @@ -17,6 +18,7 @@ import java.util.Random; import javax.lang.model.element.Modifier; +import javax.lang.model.type.DeclaredType; import static com.arellomobile.mvp.compiler.Util.decapitalizeString; @@ -32,6 +34,7 @@ public final class ViewStateClassGenerator extends JavaFilesGenerator generate(ViewInterfaceInfo viewInterfaceInfo) { ClassName viewName = viewInterfaceInfo.getName(); TypeName nameWithTypeVariables = viewInterfaceInfo.getNameWithTypeVariables(); + DeclaredType viewInterfaceType = (DeclaredType) viewInterfaceInfo.getElement().asType(); TypeSpec.Builder classBuilder = TypeSpec.classBuilder(viewName.simpleName() + MvpProcessor.VIEW_STATE_SUFFIX) .addModifiers(Modifier.PUBLIC) @@ -42,7 +45,7 @@ public List generate(ViewInterfaceInfo viewInterfaceInfo) { for (ViewMethod method : viewInterfaceInfo.getMethods()) { TypeSpec commandClass = generateCommandClass(method, nameWithTypeVariables); classBuilder.addType(commandClass); - classBuilder.addMethod(generateMethod(method, nameWithTypeVariables, commandClass)); + classBuilder.addMethod(generateMethod(viewInterfaceType, method, nameWithTypeVariables, commandClass)); } JavaFile javaFile = JavaFile.builder(viewName.packageName(), classBuilder.build()) @@ -75,7 +78,8 @@ private TypeSpec generateCommandClass(ViewMethod method, TypeName viewTypeName) return classBuilder.build(); } - private MethodSpec generateMethod(ViewMethod method, TypeName viewTypeName, TypeSpec commandClass) { + private MethodSpec generateMethod(DeclaredType enclosingType, ViewMethod method, + TypeName viewTypeName, TypeSpec commandClass) { // TODO: String commandFieldName = "$cmd"; String commandFieldName = decapitalizeString(method.getCommandClassName()); @@ -85,11 +89,11 @@ private MethodSpec generateMethod(ViewMethod method, TypeName viewTypeName, Type commandFieldName += random.nextInt(10); } - return MethodSpec.overriding(method.getElement()) + return MethodSpec.overriding(method.getElement(), enclosingType, MvpCompiler.getTypeUtils()) .addStatement("$1N $2L = new $1N($3L)", commandClass, commandFieldName, method.getArgumentsString()) .addStatement("mViewCommands.beforeApply($L)", commandFieldName) .addCode("\n") - .beginControlFlow("if (mViews == null || mViews.isEmpty())") + .beginControlFlow("if (hasNotView())") .addStatement("return") .endControlFlow() .addCode("\n") diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstateprovider/InjectViewStateProcessor.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstateprovider/InjectViewStateProcessor.java index 553730f5..185650f5 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstateprovider/InjectViewStateProcessor.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/viewstateprovider/InjectViewStateProcessor.java @@ -126,10 +126,6 @@ private String getViewClassFromGeneric(TypeElement typeElement) { Map parentTypes = Collections.emptyMap(); - if (!typeElement.getTypeParameters().isEmpty()) { - MvpCompiler.getMessager().printMessage(Diagnostic.Kind.WARNING, "Your " + typeElement.getSimpleName() + " is typed. @InjectViewState may generate wrong code. Your can set view/view state class manually."); - } - while (superclass.getKind() != TypeKind.NONE) { TypeElement superclassElement = (TypeElement) ((DeclaredType) superclass).asElement(); diff --git a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/CompilerTest.java b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/CompilerTest.java index dc4f7d33..052fe9fc 100644 --- a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/CompilerTest.java +++ b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/CompilerTest.java @@ -39,6 +39,13 @@ protected Compilation compileSourcesWithProcessor(JavaFileObject... sources) { .compile(sources); } + protected Compilation compileLibSourcesWithProcessor(String moxyReflectorPackage, JavaFileObject... sources) { + return javac() + .withOptions("-implicit:none", "-AmoxyReflectorPackage=" + moxyReflectorPackage) + .withProcessors(new MvpCompiler()) + .compile(sources); + } + /** * Asserts that all files from {@code exceptedGeneratedFiles} exists in {@code actualGeneratedFiles} * and have equivalent bytecode diff --git a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/MultiModulesTest.java b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/MultiModulesTest.java new file mode 100644 index 00000000..fe767ff8 --- /dev/null +++ b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/MultiModulesTest.java @@ -0,0 +1,62 @@ +package com.arellomobile.mvp.compiler; + +import com.google.testing.compile.Compilation; + +import org.junit.Test; + +import javax.tools.JavaFileObject; + +import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.JavaFileObjects.forResource; +import static com.google.testing.compile.JavaFileObjects.forSourceLines; + +public class MultiModulesTest extends CompilerTest { + + @Test + public void testLibraryModule() throws Exception { + JavaFileObject[] sources = { + forResource("multimodules/lib1/Lib1Presenter.java"), + forResource("multimodules/lib1/Lib1View.java") + }; + + JavaFileObject[] generatedSources = { + forResource("multimodules/lib1/Lib1Presenter$$ViewStateProvider.java"), + forResource("multimodules/lib1/Lib1View$$State.java"), + forResource("multimodules/lib1/MoxyReflector.java") + }; + + Compilation compilation = compileLibSourcesWithProcessor("multimodules.lib1", sources); + Compilation exceptedCompilation = compileSources(generatedSources); + + assertThat(compilation).succeededWithoutWarnings(); + assertExceptedFilesGenerated(compilation.generatedFiles(), exceptedCompilation.generatedFiles()); + } + + @Test + public void testRegisterMoxyReflectorPackages() throws Exception { + JavaFileObject someClientClass = forSourceLines("multimodules.app.App", + "package multimodules.app;", + "import com.arellomobile.mvp.RegisterMoxyReflectorPackages;", + "@RegisterMoxyReflectorPackages(\"multimodules.lib1\")", + "public class App {}" + ); + + JavaFileObject[] sources = new JavaFileObject[]{ + forResource("multimodules/app/AppPresenter.java"), + forResource("multimodules/app/AppView.java"), + someClientClass + }; + + JavaFileObject[] exceptedSources = new JavaFileObject[]{ + forResource("multimodules/app/AppPresenter$$ViewStateProvider.java"), + forResource("multimodules/app/AppView$$State.java"), + forResource("multimodules/app/MoxyReflector.java") + }; + + Compilation compilation = compileSourcesWithProcessor(sources); + Compilation exceptedCompilation = compileSources(exceptedSources); + + assertThat(compilation).succeededWithoutWarnings(); + assertExceptedFilesGenerated(compilation.generatedFiles(), exceptedCompilation.generatedFiles()); + } +} diff --git a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/PresentersBinderTest.java b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/PresentersBinderTest.java index f547d5e4..57fb48b7 100644 --- a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/PresentersBinderTest.java +++ b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/PresentersBinderTest.java @@ -25,7 +25,7 @@ public static String[] data() { return new String[]{ "target.SimpleInjectPresenterTarget", "target.SimpleProvidePresenterTarget", - // "target.GenericPresenterTarget", // failing, see issue #166 + "target.GenericPresenterTarget", }; } diff --git a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/ViewStateTest.java b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/ViewStateTest.java index 65826367..253fce4e 100644 --- a/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/ViewStateTest.java +++ b/moxy-compiler/src/test/java/com/arellomobile/mvp/compiler/ViewStateTest.java @@ -31,6 +31,7 @@ public static String[] data() { "view.GenericView", "view.GenericWithExtendsView", "view.GenericMethodsView", + "view.ExtendsOfGenericView", "view.strategies_inheritance.ChildView", "view.strategies_inheritance.ParentView", }; diff --git a/moxy-compiler/src/test/resources/multimodules/app/AppPresenter$$ViewStateProvider.java b/moxy-compiler/src/test/resources/multimodules/app/AppPresenter$$ViewStateProvider.java new file mode 100644 index 00000000..60033e91 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/app/AppPresenter$$ViewStateProvider.java @@ -0,0 +1,12 @@ +package multimodules.app; + +import com.arellomobile.mvp.MvpView; +import com.arellomobile.mvp.ViewStateProvider; +import com.arellomobile.mvp.viewstate.MvpViewState; + +public class AppPresenter$$ViewStateProvider extends ViewStateProvider { + @Override + public MvpViewState getViewState() { + return new AppView$$State(); + } +} diff --git a/moxy-compiler/src/test/resources/multimodules/app/AppPresenter.java b/moxy-compiler/src/test/resources/multimodules/app/AppPresenter.java new file mode 100644 index 00000000..199ebe48 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/app/AppPresenter.java @@ -0,0 +1,8 @@ +package multimodules.app; + +import com.arellomobile.mvp.InjectViewState; +import com.arellomobile.mvp.MvpPresenter; + +@InjectViewState +public class AppPresenter extends MvpPresenter { +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/app/AppView$$State.java b/moxy-compiler/src/test/resources/multimodules/app/AppView$$State.java new file mode 100644 index 00000000..e0777a94 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/app/AppView$$State.java @@ -0,0 +1,6 @@ +package multimodules.app; + +import com.arellomobile.mvp.viewstate.MvpViewState; + +public class AppView$$State extends MvpViewState implements AppView { +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/app/AppView.java b/moxy-compiler/src/test/resources/multimodules/app/AppView.java new file mode 100644 index 00000000..5ad2c6a5 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/app/AppView.java @@ -0,0 +1,6 @@ +package multimodules.app; + +import com.arellomobile.mvp.MvpView; + +public interface AppView extends MvpView { +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/app/MoxyReflector.java b/moxy-compiler/src/test/resources/multimodules/app/MoxyReflector.java new file mode 100644 index 00000000..7750af41 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/app/MoxyReflector.java @@ -0,0 +1,47 @@ +package com.arellomobile.mvp; + +import java.lang.Class; +import java.lang.Object; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import multimodules.app.AppPresenter; +import multimodules.app.AppPresenter$$ViewStateProvider; + +public final class MoxyReflector { + private static Map, Object> sViewStateProviders; + + private static Map, List> sPresenterBinders; + + private static Map, Object> sStrategies; + + static { + sViewStateProviders = new HashMap<>(); + sViewStateProviders.put(AppPresenter.class, new AppPresenter$$ViewStateProvider()); + + sPresenterBinders = new HashMap<>(); + + sStrategies = new HashMap<>(); + + sViewStateProviders.putAll(multimodules.lib1.MoxyReflector.getViewStateProviders()); + sPresenterBinders.putAll(multimodules.lib1.MoxyReflector.getPresenterBinders()); + sStrategies.putAll(multimodules.lib1.MoxyReflector.getStrategies()); + } + + public static Object getViewState(Class presenterClass) { + ViewStateProvider viewStateProvider = (ViewStateProvider) sViewStateProviders.get(presenterClass); + if (viewStateProvider == null) { + return null; + } + + return viewStateProvider.getViewState(); + } + + public static List getPresenterBinders(Class delegated) { + return sPresenterBinders.get(delegated); + } + + public static Object getStrategy(Class strategyClass) { + return sStrategies.get(strategyClass); + } +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/lib1/Lib1Presenter$$ViewStateProvider.java b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1Presenter$$ViewStateProvider.java new file mode 100644 index 00000000..c5f16557 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1Presenter$$ViewStateProvider.java @@ -0,0 +1,12 @@ +package multimodules.lib1; + +import com.arellomobile.mvp.MvpView; +import com.arellomobile.mvp.ViewStateProvider; +import com.arellomobile.mvp.viewstate.MvpViewState; + +public class Lib1Presenter$$ViewStateProvider extends ViewStateProvider { + @Override + public MvpViewState getViewState() { + return new Lib1View$$State(); + } +} diff --git a/moxy-compiler/src/test/resources/multimodules/lib1/Lib1Presenter.java b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1Presenter.java new file mode 100644 index 00000000..ff74d536 --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1Presenter.java @@ -0,0 +1,8 @@ +package multimodules.lib1; + +import com.arellomobile.mvp.InjectViewState; +import com.arellomobile.mvp.MvpPresenter; + +@InjectViewState +public class Lib1Presenter extends MvpPresenter { +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/lib1/Lib1View$$State.java b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1View$$State.java new file mode 100644 index 00000000..d8f5495f --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1View$$State.java @@ -0,0 +1,6 @@ +package multimodules.lib1; + +import com.arellomobile.mvp.viewstate.MvpViewState; + +public class Lib1View$$State extends MvpViewState implements Lib1View { +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/lib1/Lib1View.java b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1View.java new file mode 100644 index 00000000..9460cb0a --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/lib1/Lib1View.java @@ -0,0 +1,6 @@ +package multimodules.lib1; + +import com.arellomobile.mvp.MvpView; + +public interface Lib1View extends MvpView { +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/multimodules/lib1/MoxyReflector.java b/moxy-compiler/src/test/resources/multimodules/lib1/MoxyReflector.java new file mode 100644 index 00000000..be9b3a2b --- /dev/null +++ b/moxy-compiler/src/test/resources/multimodules/lib1/MoxyReflector.java @@ -0,0 +1,34 @@ +package multimodules.lib1; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class MoxyReflector { + private static Map, Object> sViewStateProviders; + + private static Map, List> sPresenterBinders; + + private static Map, Object> sStrategies; + + static { + sViewStateProviders = new HashMap<>(); + sViewStateProviders.put(Lib1Presenter.class, new Lib1Presenter$$ViewStateProvider()); + + sPresenterBinders = new HashMap<>(); + + sStrategies = new HashMap<>(); + } + + public static Map, Object> getViewStateProviders() { + return sViewStateProviders; + } + + public static Map, List> getPresenterBinders() { + return sPresenterBinders; + } + + public static Map, Object> getStrategies() { + return sStrategies; + } +} diff --git a/moxy-compiler/src/test/resources/target/GenericPresenterTarget$$PresentersBinder.java b/moxy-compiler/src/test/resources/target/GenericPresenterTarget$$PresentersBinder.java index cb55f14a..cc754a97 100644 --- a/moxy-compiler/src/test/resources/target/GenericPresenterTarget$$PresentersBinder.java +++ b/moxy-compiler/src/test/resources/target/GenericPresenterTarget$$PresentersBinder.java @@ -12,7 +12,7 @@ public class GenericPresenterTarget$$PresentersBinder extends PresenterBinder { public List> getPresenterFields() { - List> presenters = new ArrayList<>(); + List> presenters = new ArrayList<>(1); presenters.add(new presenterBinder()); diff --git a/moxy-compiler/src/test/resources/target/GenericPresenterTarget.java b/moxy-compiler/src/test/resources/target/GenericPresenterTarget.java index 2a98d323..1d20d27c 100644 --- a/moxy-compiler/src/test/resources/target/GenericPresenterTarget.java +++ b/moxy-compiler/src/test/resources/target/GenericPresenterTarget.java @@ -9,7 +9,7 @@ public class GenericPresenterTarget implements GenericView { @InjectPresenter - GenericPresenter mPresenter; + GenericPresenter presenter; @ProvidePresenter GenericPresenter providePresenter() { diff --git a/moxy-compiler/src/test/resources/view/ExtendsOfGenericView$$State.java b/moxy-compiler/src/test/resources/view/ExtendsOfGenericView$$State.java new file mode 100644 index 00000000..1d3b5f8f --- /dev/null +++ b/moxy-compiler/src/test/resources/view/ExtendsOfGenericView$$State.java @@ -0,0 +1,40 @@ +package view; + +import com.arellomobile.mvp.viewstate.MvpViewState; +import com.arellomobile.mvp.viewstate.ViewCommand; +import com.arellomobile.mvp.viewstate.strategy.AddToEndStrategy; +import java.io.Serializable; +import java.lang.Override; + +public class ExtendsOfGenericView$$State extends MvpViewState implements ExtendsOfGenericView { + @Override + public void testEvent(Serializable param) { + TestEventCommand testEventCommand = new TestEventCommand(param); + mViewCommands.beforeApply(testEventCommand); + + if (mViews == null || mViews.isEmpty()) { + return; + } + + for (ExtendsOfGenericView view : mViews) { + view.testEvent(param); + } + + mViewCommands.afterApply(testEventCommand); + } + + public class TestEventCommand extends ViewCommand { + public final Serializable param; + + TestEventCommand(Serializable param) { + super("testEvent", AddToEndStrategy.class); + + this.param = param; + } + + @Override + public void apply(ExtendsOfGenericView mvpView) { + mvpView.testEvent(param); + } + } +} \ No newline at end of file diff --git a/moxy-compiler/src/test/resources/view/ExtendsOfGenericView.java b/moxy-compiler/src/test/resources/view/ExtendsOfGenericView.java new file mode 100644 index 00000000..da6edab2 --- /dev/null +++ b/moxy-compiler/src/test/resources/view/ExtendsOfGenericView.java @@ -0,0 +1,8 @@ +package view; + +import com.arellomobile.mvp.MvpView; + +import java.io.Serializable; + +public interface ExtendsOfGenericView extends GenericWithExtendsView { +} diff --git a/moxy-material/build.gradle b/moxy-material/build.gradle new file mode 100755 index 00000000..bd325249 --- /dev/null +++ b/moxy-material/build.gradle @@ -0,0 +1,238 @@ +apply plugin: 'java' +apply plugin: 'maven-publish' +apply plugin: 'maven' + +ext { + publishDir = 'artifacts' + publishMavenDir = 'artifacts-maven' + targetGroupId = 'tech.schoolhelper' + targetArtefactId = 'moxy-x-material' + targetArtefactIdDev = 'moxy-material-dev' + versionFile = 'VERSION' +} +configurations { + javadocDeps +} + +// This is important even if Android Studio claims it isn't +// used. Android can't interpret Java 8 byte code. +sourceCompatibility = JavaVersion.VERSION_1_7 +targetCompatibility = JavaVersion.VERSION_1_7 + +def logger = new com.android.build.gradle.internal.LoggerWrapper(project.logger) +def sdkHandler = new com.android.build.gradle.internal.SdkHandler(project, logger) +for (File file : sdkHandler.sdkLoader.repositories) { + project.repositories.maven { + url = file.toURI() + } +} + +task clearPublishDir(type: Delete) { + delete publishDir +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task makeJavadocs(type: Javadoc) { + classpath += configurations.javadocDeps + source = sourceSets.main.allJava +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +task makeJar(type: Jar, dependsOn: classes) { + from('build/classes/java/main') +} + +task copyVersion { + doLast { + def versionFile = new File(project.rootDir, versionFile) + versionFile.write(targetVersionName) + + [publishDir, publishMavenDir].each { dest -> + copy { + from versionFile + into dest + } + } + } +} + +def getBasePom(String libName) { + + def basePom = pom { + //noinspection GroovyAssignabilityCheck + project { + groupId targetGroupId + artifactId targetArtefactId + version targetVersionName + name 'Moxy-androidx' + modelVersion = '4.0.0' + description = POM_DESCRIPTION + url = URL + packaging = 'jar' + + inceptionYear '2016' + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution 'repo' + } + } + + developers { + developer { + name 'Vova Stelmashchuk' + email 'vovochkastelmashchuk@gmail.com' + organization = 'SchoolHelper' + organizationUrl URL + } + } + + scm { + connection POM_SCM_CONNECTION + developerConnection POM_SCM_CONNECTION + url POM_URL + } + }.withXml { + asNode().appendNode('packaging', 'jar') + } + } + + return basePom.withXml { + asNode().dependencies.'*'.findAll { + it.artifactId.text().contains('moxy') + }.each { + it.groupId*.value = targetGroupId + it.version*.value = targetVersionName + } + } +} + +task copyJars { + doLast { + def buildDir = 'build/libs/' + copy { + from buildDir + into publishMavenDir + } + + copy { + from(buildDir) + { + rename targetArtefactId + '(.*)', targetArtefactIdDev + '$1' + } + into publishDir + } + } +} + + +task createPomXml { + doLast { + // creating local pom + + getBasePom('moxy-dev').withXml { + asNode().appendNode('build').appendNode('plugins').with { + appendNode('plugin').with { + appendNode('groupId', 'org.apache.maven.plugins') + appendNode('artifactId', 'maven-gpg-plugin') + appendNode('version', '1.5') + appendNode('executions').with { + appendNode('execution').with { + appendNode('id', 'sign-artifacts') + appendNode('phase', 'verify') + appendNode('goals').with { + appendNode('goal', 'sign') + } + } + } + appendNode('configuration').appendNode('gpgArguments').appendNode('arg', '--no-tty') + } + } + + asNode().artifactId*.value = targetArtefactIdDev + + asNode().dependencies.'*'.findAll() { + it.version.text() == '4.0.+' && project.configurations.compile.allDependencies.find { dep -> + dep.name == it.artifactId.text() + } + }.each() { + it.version*.value = '[4.0,)' + } + + + }.writeTo(publishDir + "/" + targetArtefactIdDev + ".pom") + + //creating global pom + + getBasePom('moxy').withXml { + + asNode().appendNode('build') + .appendNode('plugins') + + + }.writeTo(publishMavenDir + "/" + targetArtefactId + ".pom") + + getBasePom('moxy').withXml { + + asNode().appendNode('build') + .appendNode('plugins') + .appendNode('plugin') + .with { + appendNode('groupId', 'org.sonatype.plugins') + appendNode('artifactId', 'nexus-staging-maven-plugin') + appendNode('version', '1.6.4') + appendNode('extensions', 'true') + appendNode('configuration').with { + appendNode('serverId', 'ossrh') + appendNode('nexusUrl', 'https://oss.sonatype.org/') + appendNode('autoReleaseAfterClose', 'false') + } + } + + asNode().appendNode('distributionManagement').with { + appendNode('snapshotRepository') + .with { + appendNode('id', 'ossrh') + appendNode('url', 'https://oss.sonatype.org/content/repositories/snapshots') + } + appendNode('repository') + .with { + appendNode('id', 'ossrh') + appendNode('url', 'https://oss.sonatype.org/service/local/staging/deploy/maven2/') + } + } + + }.writeTo(publishMavenDir + "/pom.xml") + + } +} + +compileJava.dependsOn clearPublishDir +copyJars.dependsOn makeJar +copyJars.dependsOn sourcesJar +javadocJar.dependsOn makeJavadocs +copyJars.dependsOn javadocJar +copyVersion.dependsOn copyJars +copyVersion.dependsOn createPomXml +assemble.dependsOn copyVersion + +dependencies { + implementation project(':moxy-x') + + compileOnly deps.android + compileOnly project(':stub-androidx') + compileOnly project(':stub-material') + + javadocDeps project(':moxy-x') + javadocDeps deps.android + javadocDeps project(':stub-androidx') + javadocDeps project(':stub-material') +} \ No newline at end of file diff --git a/moxy-material/src/main/java/com/arellomobile/mvp/MvpBottomSheetDialogFragment.java b/moxy-material/src/main/java/com/arellomobile/mvp/MvpBottomSheetDialogFragment.java new file mode 100644 index 00000000..ce567891 --- /dev/null +++ b/moxy-material/src/main/java/com/arellomobile/mvp/MvpBottomSheetDialogFragment.java @@ -0,0 +1,96 @@ +package com.arellomobile.mvp; + +import android.os.Bundle; + +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; + +import androidx.fragment.app.Fragment; + +public class MvpBottomSheetDialogFragment extends BottomSheetDialogFragment { + + private boolean mIsStateSaved; + + private MvpDelegate mMvpDelegate; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getMvpDelegate().onCreate(savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + + mIsStateSaved = false; + + getMvpDelegate().onAttach(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + mIsStateSaved = true; + + getMvpDelegate().onSaveInstanceState(outState); + getMvpDelegate().onDetach(); + } + + @Override + public void onStop() { + super.onStop(); + + getMvpDelegate().onDetach(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + getMvpDelegate().onDetach(); + getMvpDelegate().onDestroyView(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + //We leave the screen and respectively all fragments will be destroyed + if (getActivity().isFinishing()) { + getMvpDelegate().onDestroy(); + return; + } + + // When we rotate device isRemoving() return true for fragment placed in backstack + // http://stackoverflow.com/questions/34649126/fragment-back-stack-and-isremoving + if (mIsStateSaved) { + mIsStateSaved = false; + return; + } + + // See https://github.com/Arello-Mobile/Moxy/issues/24 + boolean anyParentIsRemoving = false; + Fragment parent = getParentFragment(); + while (!anyParentIsRemoving && parent != null) { + anyParentIsRemoving = parent.isRemoving(); + parent = parent.getParentFragment(); + } + + if (isRemoving() || anyParentIsRemoving) { + getMvpDelegate().onDestroy(); + } + } + + /** + * @return The {@link MvpDelegate} being used by this Fragment. + */ + public MvpDelegate getMvpDelegate() { + if (mMvpDelegate == null) { + mMvpDelegate = new MvpDelegate<>(this); + } + + return mMvpDelegate; + } +} diff --git a/moxy-material/stub-material/build.gradle b/moxy-material/stub-material/build.gradle new file mode 100755 index 00000000..222d4acf --- /dev/null +++ b/moxy-material/stub-material/build.gradle @@ -0,0 +1,9 @@ +apply plugin: 'java' + +sourceCompatibility = JavaVersion.VERSION_1_7 +targetCompatibility = JavaVersion.VERSION_1_7 + +dependencies { + compileOnly deps.android + compileOnly project(':stub-androidx') +} \ No newline at end of file diff --git a/moxy-material/stub-material/src/main/java/com/google/android/material/bottomsheet/BottomSheetDialogFragment.java b/moxy-material/stub-material/src/main/java/com/google/android/material/bottomsheet/BottomSheetDialogFragment.java new file mode 100644 index 00000000..c5a3fcec --- /dev/null +++ b/moxy-material/stub-material/src/main/java/com/google/android/material/bottomsheet/BottomSheetDialogFragment.java @@ -0,0 +1,38 @@ +package com.google.android.material.bottomsheet; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatDialogFragment; +import androidx.fragment.app.Fragment; + +public class BottomSheetDialogFragment extends AppCompatDialogFragment { + + public void onCreate(Bundle savedInstanceState) { + throw new RuntimeException("Stub!"); + } + + public void onResume() { + throw new RuntimeException("Stub!"); + } + + public void onSaveInstanceState(Bundle outState) { + throw new RuntimeException("Stub!"); + } + + public void onStop() { + throw new RuntimeException("Stub!"); + } + + public void onDestroyView() { + throw new RuntimeException("Stub!"); + } + + public void onDestroy() { + throw new RuntimeException("Stub!"); + } + + public Fragment getParentFragment() { + throw new RuntimeException("Stub!"); + } + +} diff --git a/moxy-templates/Kotlin/MoxyActivity/template.xml b/moxy-templates/Kotlin/MoxyActivity/template.xml index 803a3cd8..0fb481e4 100644 --- a/moxy-templates/Kotlin/MoxyActivity/template.xml +++ b/moxy-templates/Kotlin/MoxyActivity/template.xml @@ -1,6 +1,6 @@