Giter Club home page Giter Club logo

moxy's Introduction

Moxy

Maven Central

Moxy 2 is out! Check out the migration guide and give it a try!

Moxy is a library that allows for hassle-free implementation of the MVP pattern in an Android Application. Without troubles of lifecycle and boilerplate code!

Illustration of how Moxy is working: schematic_using

Capabilities

Moxy has a few killer features:

  • Presenter stays alive when Activity is being recreated (it simplifies multithread handling)
  • Automatical restoration of all content in the recreated Activity (including additional dynamic content)

Example

View interface

interface MainView : MvpView {

    @AddToEndSingle
    fun displayUser(user: User)
}

class MainActivity : MvpAppCompatActivity(R.layout.activity_main), MainView {

    private val presenter by moxyPresenter { MainPresenter() }

    override fun displayUser(user: User) {
        userLayout.showUser(user)
    }
}

Presenter

class MainPresenter : MvpPresenter<MainView>() {
    override fun onFirstViewAttach() {
        viewState.showUser(getCurrentUser())
    }
}

Inject with Dagger2

Kotlin

@Inject
lateinit var presenterProvider: Provider<MainPresenter>

private val presenter by moxyPresenter { presenterProvider.get() }

Java

@InjectPresenter
MainPresenter presenter;

@Inject
Provider<MainPresenter> presenterProvider;

@ProvidePresenter
MainPresenter providePresenter() {
    return presenterProvider.get();
}

Android studio and Intellij templates

We will change this template in future In order to avoid tedious task of writing boilerplate code for binding activities, fragments and its presentation parts, we recommend use of Android Studio templates for Moxy.

Links

Telegram channels from original moxy community

Telegram channel (en)
Telegram channel (ru)

Integration

Please replace moxyVersion with the latest version number: Bintray

Base modules

Java

dependencies {
    // ...
    implementation "com.github.moxy-community:moxy:$moxyVersion"
    annotationProcessor "com.github.moxy-community:moxy-compiler:$moxyVersion"
}

Kotlin

apply plugin: 'kotlin-kapt'
// ...
dependencies {
    // ...
    implementation "com.github.moxy-community:moxy:$moxyVersion"
    kapt "com.github.moxy-community:moxy-compiler:$moxyVersion"
}

Java 8

Moxy uses Java 8 features, so you also need to specify source and target compatibility for each Module that uses Moxy:

android {
    ...
    // For Java projects
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Default Android module

For additional base view classes MvpActivity and MvpFragment add this:

implementation "com.github.moxy-community:moxy-android:$moxyVersion"

AppCompat module

If you are using AppCompat, you'll need MvpAppCompatActivity and MvpAppCompatFragment classes. Add this:

implementation "com.github.moxy-community:moxy-app-compat:$moxyVersion"

AndroidX module

If you're using AndroidX, you'll need a different implementation for MvpAppCompatActivity and MvpAppCompatFragment classes. Use this one:

implementation "com.github.moxy-community:moxy-androidx:$moxyVersion"

AndroidX (Google material) module

If you're using Google material, use MvpBottomSheetDialogFragment and add this:

implementation "com.github.moxy-community:moxy-material:$moxyVersion"

Kotlin extensions

Declare presenters in your views using property delegate:

class MyFragment: MvpFragment() {
    ...
    private val presenter by moxyPresenter { presenterProvider.get() }
    ...
}

Launch coroutines in presenter scope:

class MyPresenter : MvpPresenter<MvpView>() {
    override fun onFirstViewAttach() {
        presenterScope.launch {
            // Coroutine that will be canceled when presenter is destroyed
        }
    }
}

To use MvpDelegateHolder.moxyPresenter and MvpPresenter.presenterScope, add this:

implementation "com.github.moxy-community:moxy-ktx:$moxyVersion"

New Features and Compiler option for Migration from old version

By default, each MvpView method must have an annotation @StateStrategyType. In the version 1 of Moxy it was allowed to omit stategies for methods. In this case a default strategy was applied.

You can fallback to the old behavior. To do this, set the disableEmptyStrategyCheck parameter to true.

disableEmptyStrategyCheck : 'true'

In this case the default strategy will be AddToEndSingleStrategy. In the old version the default strategy was AddToEndStrategy.

To change default strategy provide for the defaultMoxyStrategy parameter a full class name of the new default strategy.

defaultMoxyStrategy : 'moxy.viewstate.strategy.OneExecutionStateStrategy'

If the compiler finds MvpView method without the @StateStrategyType annotation, it'd show an error via standard method for notifying about compilation problems. For ease of migration from older versions we have provided an additional mechanism: EmptyStrategyHelper. It collects all the errors associated with an empty strategy in one place. Using it, you can easily navigate from the EmptyStrategyHelper directly to the method with a missing strategy.

To switch the error output method enable this option

enableEmptyStrategyHelper : 'true'

To enable Isolating incremental annotation processor mode for Moxy processor use this option

moxyEnableIsolatingProcessing : 'true'

Use this option for faster incremental builds. You can read about differences between Isolating and Aggreagating modes and Gradle incremental annotation processing support in Gradle documentation. Warning! This option is experimental for now. It should work fine, but we prefer to make this transition as safe as possible. If you'll encounter compilation problems after enabling this option, please feel free to report an ussue. Hopefully we will enable isolating annotation processor mode by default after several releases.

How to correctly use compilation flags check out the sample-app build.gradle file

Plugin

This plugin automates work with strategies You can download it here https://plugins.jetbrains.com/plugin/13679-moxy-strategy/versions developing here https://github.com/Maksim-Novikov/moxy-strategy-plugin

ProGuard\R8

If you are using R8 then no additional configuration required. If you use ProGuard then you have to manually add rules from this file.

Road Map

  • [✓] Provide a migration tool from com.arello-mobile.moxy and its default strategy
  • [✓] Kotlin incremental compilation support
  • [✓] Remove reflectors and common presenter store
  • [✓] Add delivery module support
  • [х] Add separate Annotation Processor for migration
  • [✓] Research possibility of removing @InjectViewState annotation
  • Provide Runtime Implementation

Moxy Community

Brave people who created the library:

@senneco
@jordan1997
@xanderblinov
@VovaStelmashchuk
@aasitnikov
@alaershov
@bejibx
@katkoff
@ekursakov
@SavinMike
@sychyow
@AlexeyKorshun
@dmdevgo
@rsajob
@terrakok
@mohaxspb
@CherryPerry
@fl1pflops
@phoenixxt
@Dosssik
@AcoustickSan
@seven332
@v-grishechko
@sbogolepov
@A-Zaiats
@lion4ik
@NotPerfectBlue
@IlyaGulya
@Svechnikov
@hram
@ValdZX
@Maksim-Novikov

You may also find them in contributors page of the old project

Contributing

Install code style to your IntelliJ or Android Studio. MoxyAndroid.xml For import file use or Mac OS: Preferences -> Editor -> Code style -> Scheme -> Import Scheme -> IntelliJ IDEA code style XML -> choose the MoxyAndroid.xml file in finder

License

The MIT License (MIT)

Copyright (c) 2019 Moxy Community

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

moxy's People

Contributors

aasitnikov avatar alaershov avatar dekan avatar ilyagulya avatar katkoff avatar maksim-novikov avatar sitnikovimprove avatar svechnikov avatar sychyow avatar valdzx avatar vkotovv avatar vovastelmashchuk avatar xanderblinov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

moxy's Issues

I update the list in the adapter, which is in the presenter. Bug or feature?

In the presenter, I create a list, receiving data from the interactor, load it into the view through the "setListAdapter" method to add it to the adapter. And so I can update the list in the presenter and pull the "notifyNoteItemList" method from view to update it in the adapter. Is this normal behavior or am I doing something wrong?)

NotesPresenter:

public class NotesPresenter extends MvpPresenter<NotesMvp.view> implements NotesMvp.presenter {

List<Note> noteList;
   ........

    @Override
    public void onCreateView() {
        noteList = interactor.getNoteList();
        Collections.reverse(noteList);
        getViewState().setListAdapter(noteList);
        checkEmptyList();
    }

    @Override
    public void onResultNoteEditor(int result) {
        if (result == RESULT_OK) {
            interactor.getNote(interactor.getCurrentNote(), dataList -> {
                int index = getNoteItemIndex(dataList.get(0).getNoteID());

                switch (interactor.getNoteState()) {
                    case STATE_CREATE:
                        noteList.addAll(dataList);
                        break;
                    case STATE_EDIT:
                        noteList.set(index, dataList.get(0));
                        break;
                    case STATE_DELETE:
                        noteList.remove(index);
                        break;
                }
            });
            getViewState().notifyNoteItemList();
            checkEmptyList();
        }
    }

    private int getNoteItemIndex(int noteID) {
        Log.d("lol", "find note: " + noteID);
        for (int i = 0; i < noteList.size(); i++) {
            Note note = noteList.get(i);
            if (note.getNoteID() == noteID) {
                return i;
            }
        }
        return -1;
    }
}

NotesFragment:

public class NotesFragment extends MvpAppCompatFragment implements NotesMvp.view {

........

    @Override
    public void setListAdapter(List<Note> noteList) {
        this.noteList = noteList;
        Log.d("lol", "LIST SIZE: " + this.noteList.size());
        adapterNote = new NoteAdapter(getActivity(), noteList, presenter);
        recyclerView.setAdapter(adapterNote);
    }

    @Override
    public void notifyNoteItemList() {
        adapterNote.notifyDataSetChanged();
    }

    @Override
    public void onResultNoteEditor(int result) {
        presenter.onResultNoteEditor(result);
    }
........
}

It is important for me to keep the list in the presenter in order to find the desired item or find out the size of the list. Maybe it needs to be done somehow differently.
I would be grateful for your help!

Add state strategy aliases

Allow @StateStrategyType annotation to be placed on other annotation. In that case this annotation becomes alias for @StateStrategyType.

Let's say we have following annotation defined:

@StateStrategyType(OneExecutionStateStrategy::class)
annotation class OneExecution

If that annotation is placed on some view method, it will have the same effect, as placing @StateStrategyType annotation. For example, this two methods will have same strategies.

interface MyView: MvpView {
    
    @StateStrategyType(OneExecutionStateStrategy::class)
    fun oneExecution()
    
    @OneExecution
    fun oneExecution2()
}

NullPointerException at moxy.MvpAppCompatActivity.onStart

There are multiple crashes in Google Play Console with no stacktrace of the application and only visible problem in moxy.MvpAppCompatActivity.onStart. Here it calls super.onStart(), where super is AppCompatActivity.
Moxy version is 2.0.0. However, no changes in this class were made since then.

Possible fix could be wrap this call with check on null. We will try to up version and monitor the situation.

Full stacktrace:

java.lang.RuntimeException: 
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2583)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2665)
  at android.app.ActivityThread.-wrap11 (ActivityThread.java)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1499)
  at android.os.Handler.dispatchMessage (Handler.java:111)
  at android.os.Looper.loop (Looper.java:207)
  at android.app.ActivityThread.main (ActivityThread.java:5765)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:798)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:688)
  at com.android.internal.os.Device.fp (Device.java)
Caused by: java.lang.NullPointerException: 
  at androidx.fragment.app.DialogFragment.onActivityCreated (DialogFragment.java:417)
  at androidx.fragment.app.Fragment.performActivityCreated (Fragment.java:2460)
  at androidx.fragment.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1483)
  at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState (FragmentManagerImpl.java:1784)
  at androidx.fragment.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1852)
  at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange (FragmentManagerImpl.java:3269)
  at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated (FragmentManagerImpl.java:3229)
  at androidx.fragment.app.FragmentController.dispatchActivityCreated (FragmentController.java:201)
  at androidx.fragment.app.FragmentActivity.onStart (FragmentActivity.java:620)
  at androidx.appcompat.app.AppCompatActivity.onStart (AppCompatActivity.java:178)
  at moxy.MvpAppCompatActivity.onStart (MvpAppCompatActivity.java:32)
  at android.app.Instrumentation.callActivityOnStart (Instrumentation.java:1245)
  at android.app.Activity.performStart (Activity.java:6364)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2540)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2665)
  at android.app.ActivityThread.-wrap11 (ActivityThread.java)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1499)
  at android.os.Handler.dispatchMessage (Handler.java:111)
  at android.os.Looper.loop (Looper.java:207)
  at android.app.ActivityThread.main (ActivityThread.java:5765)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:798)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:688)
  at com.android.internal.os.Device.fp (Device.java)

Inject with dagger2

Trying to use Moxy + Dagger2.
@ProvidePresenter fun doesn't trigger and as result presenter cant be initialized.
What's i doing wrong?

FragmentModule:

@Module
class ShowcaseFragmentModule {
    @Provides
    fun provideShowcaseFragmentPresenter(): ShowcaseFragmentPresenter = ShowcaseFragmentPresenter()
}

Fragment:

class ShowcaseFragment : MvpAppCompatFragment(), Injectable, ShowcaseFragmentView {
    @Inject
    lateinit var daggerPresenter: Lazy<ShowcaseFragmentPresenter>

    @InjectPresenter
    lateinit var presenter: ShowcaseFragmentPresenter

    @ProvidePresenter
    fun providePresenter(): ShowcaseFragmentPresenter = daggerPresenter.get()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View =
        inflater.inflate(R.layout.fragment_showcases, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        presenter.test()
    }

    override fun text(str: String) {
        text.text = str
    }
}

View:

interface ShowcaseFragmentView : MvpView {
    fun text(str: String)
}

Presenter:

@InjectViewState
class ShowcaseFragmentPresenter : MvpPresenter<ShowcaseFragmentView>() {

    override fun onFirstViewAttach() {
        super.onFirstViewAttach()
    }

    fun test() {
        viewState.text("abcde")
    }
}

Deps:

ext.moxy_ver = '2.0.2'
    ext.dagger_ver = '2.25.2'
//moxy
    implementation "com.github.moxy-community:moxy-androidx:$moxy_ver"
    implementation "com.github.moxy-community:moxy-material:$moxy_ver"
    kapt "com.github.moxy-community:moxy-compiler:$moxy_ver"
//dagger 
implementation "com.google.dagger:dagger:$dagger_ver"
    implementation "com.google.dagger:dagger-android-support:$dagger_ver"
    kapt "com.google.dagger:dagger-compiler:$dagger_ver"
    kapt "com.google.dagger:dagger-android-processor:$dagger_ver"

Add step by step migration guide

For example:

  1. If you already using moxy-community, skip to step 3
  2. Replace in path 'com.arello-mobile' to 'com.github.moxy-community'
  3. Replace in path all 'com.arellomobile.mvp' на 'moxy'
  4. Raise moxy version to latest (2.0-alpha05)
    3.5. Moxy is still in alpha, so add to root bulid.gradle
    repositories {
    maven { url "https://dl.bintray.com/moxy-community/maven" }
    }
    4a. Fix view interfaces without strategies with help of EmptyStrategyHelper
    4b. Or, as an option, imitate old moxy behaviour: (root build.gradle)
    subprojects {
    afterEvaluate { project ->
    if (project.hasProperty('android')) {
    android.defaultConfig.javaCompileOptions.annotationProcessorOptions.arguments = [
    disableEmptyStrategyCheck: 'true',
    enableEmptyStrategyHelper: 'false',
    defaultMoxyStrategy: 'moxy.viewstate.strategy.AddToEndStrategy'
    ]
    }
    }
    }

NOTES:
No mention of global presenters

Fix broken jitpack publishing

Jitpack places all subprojects into single dependency. Not just it unnecessary increases size of users apk, but it can break compilation, as annotation processors cannot be placed in compile classpath in later versions of gradle. There's pom of moxy artifact.

That way, jitpack publishing should be either blocked or fixed.

Add internal logging capability

For debugging purposes, it would be cool to log all "internal" operations of Moxy, such as presenter attachment and detachment, and ViewState commands execution. Maybe we should allow the user to implement some Logger interface and pass it somewhere to Moxy configuration.

Change strategy inheritance algorithm

In current compiler implementation child interface state strategy propogated to superinterface, which can be source of hard to find bugs.

Consider following snippet:

interface ErrorView : MvpView {
    fun showErrorDialog(error: String)
}

@StateStrategyType(OneExecutionStateStrategy::class)
interface OneExecutionView : ErrorView {
    fun oneExecutionFunction()
}

@StateStrategyType(AddToEndSingleStrategy::class)
interface AddToEndSingleView : ErrorView {
    fun addToEndFunction()
}

Note that the ErrorView interface does not define any strategy, and two different child interfaces define different strategies.
In the current implementation generated viewstates will have following state strategies:

class AddToEndSingleView$$State : AddToEndSingleView {
    fun addToEndFunction() -> AddToEndSingleStrategy::class
    fun showErrorDialog(error: String) -> AddToEndSingleStrategy::class
}

class OneExecutionView$$State : OneExecutionView {
    fun oneExecutionFunction() ->  OneExecutionStateStrategy:class
    fun showErrorDialog(String error) -> OneExecutionStateStrategy::class
}

As you can see method showErrorDialog() has different strategies in different views.

Superinterface strategies should be evaluated without influence from child interfaces and vice-versa. So following behaviour proposed:

  1. Each view interface must have all strategies defined in it, even if it's not used directly and only used as superinteface.
  2. If interface has methods without strategies, compilation should fail.
  3. If superinterface method is overriden by child interface, than it should have child strategy, whether it's defined directly on the method or on the child interface. Methods are compared by name and parameter types.
  4. Superinterface strategy is not inherited. If child interface has no interface strategy and some method in it is defined without any strategy, than compilation should fail.

Build error "A View method has no strategy!" when view interface override with strategy.

Sample:

            public interface OtherModuleView {
                void helloFromOtherModule();
                void helloFromOtherModule(String reload);
            }
            public interface MainView extends MvpView, OtherModuleView {
                @StateStrategyType(AddToEndStrategy.class)
                void printLog(String msg);
            
                @StateStrategyType(OneExecutionStateStrategy.class)
                void openKtxActivity();
            
                @StateStrategyType(AddToEndStrategy.class)
                @Override
                void helloFromOtherModule();
            
                @StateStrategyType(AddToEndStrategy.class)
                @Override
                void helloFromOtherModule(String reload);
            }

getMvpDelegate().onDestroy() not called in MvpAppCompatFragment in some cases

For reproducing
State 1. Activity -> FullScreenFragment1
Then run

        fragmentTransaction
            .replace(fragmentContainerId, FullScreenFragment2, screen.screenKey)
            .addToBackStack(screen.screenKey)
            .commit()

State 2. Activity -> FullScreenFragment1 -> FullScreenFragment2
Collapse the app and restore it.
Then remove all fragments and add another.

Log for FullScreenFragment1

        Start FullScreenFragment1

FullScreenFragment1.onAttach()
FullScreenFragment1.providePresenter()
FullScreenFragment1.onCreateView()
FullScreenFragment1.onViewCreated()
FullScreenFragment1.onStart()
FullScreenFragment1.onResume()

        Start FullScreenFragment2

FullScreenFragment1.onPause()
FullScreenFragment1.onStop()
FullScreenFragment1.onDestroyView()

        Hide

FullScreenFragment1.onSaveInstanceState() isStateSaved = true

        Restore

nothing called for  FullScreenFragment1

        Go away

FullScreenFragment1.onDestroy() isStateSaved == true
FullScreenFragment1.onDetach()

As a result getMvpDelegate().onDestroy() will not be called for FullScreenFragment1
This is because onStart() not called for fragments in backstack and variable isStateSaved will be true.

Possible solution is the using activity.isChangingConfigurations() instead of isStateSaved

Problem with enum in viewState

Sometime moxy generated viewState with wrong type for enums.
In my case:

  1. I have a class Salary with inner Enum classes.
  2. I have an interface of view SalaryBaseView inherited from BaseView
@StateStrategyType(OneExecutionStateStrategy::class)
interface SalaryBaseView: BaseView{
    fun showBottomDialog(salaryId: Long, type: Salary.Type, items: List<SalaryReferenceMethod>)

    fun openInputEmailDialog(id: String)
}
  1. From SalaryBaseView inherited SalaryPaymentDetailView
@StateStrategyType(AddToEndSingleStrategy::class)
interface SalaryPaymentDetailView : SalaryBaseView {

    fun initListeners()

    fun setTitleToolbar(name: String)
....e.t.c.

SalaryPaymentDetailView doesn't use Salary in functions.

Problem appears in generated viewState SalaryPaymentDetailView$$State, Salary.Type replaced on Salary$Type in compile time.

Trace of error:
....\presentation\view\SalaryPaymentDetailView$$State.java:11: error: cannot find symbol
import ....\model.Salary$Type;
^
symbol: class Salary$Type
location: package ...\presentation\view\SalaryPaymentDetailView$$State.java:289: error: cannot find symbol
public void showBottomDialog(long arg0, Salary$Type arg1,
^
symbol: class Salary$Type
location: class SalaryPaymentDetailView$$State...\presentation\view\SalaryPaymentDetailView$$State.java:698: error: cannot find symbol
public final Salary$Type arg1;
^
symbol: class Salary$Type
location: class SalaryPaymentDetailView$$State.ShowBottomDialogCommand...\presentation\view\SalaryPaymentDetailView$$State.java:702: error: cannot find symbol
ShowBottomDialogCommand(long arg0, Salary$Type arg1, List<? extends SalaryReferenceMethod> arg2) {
^
symbol: class Salary$Type
location: class SalaryPaymentDetailView$$State.ShowBottomDialogCommand...\presentation\view\SalaryCardDetailView$$State.java:10: error: cannot find symbol

Same error was in 2.0.2 and 2.1.2

Package name must not start with a capital letter

when I put java classes in a folder (denchic45.myapp.ManagerDO.Note.Editor) , moxy refuses to work.
I get such an error

D:\DENIS\myApp\app\build\generated\ap_generated_sources\debug\out\denchic45\myapp\ManagerDO(\Note\Editor\NoteEditorPresenter$$ViewStateProvider.java:3: error: cannot find symbol
import denchic45.myapp.ManagerDO.Note;
                                 ^
  symbol:   class Note

If I pull it out of this folder, everything works well, what could be the problem?

Consider making MvpCompiler isolating

Current incremental type of MvpCompiler is aggregating.

It is to be researched if MvpCompiler can be made isolating, because there is no direct 1 to 1 mapping between source file with annotation and generated file.

Also there's some problems to be solved:

  • Every generated file should have exactly one originating element. Gradle docs have no mention, should these files have annotation to trigger annotation processor.
  • ViewState class is generated only if presenter with @InjectViewState references it. You can refer to #24 for more info

Androidx Presenter null

I am using androidx.Added library.Activity extended MvpAppCompatActivity.but Presenter null if I want to use it it is null

Incremental compilation support seems implemented incorrectly to me

For proper support of incremental compilation you should provide originating elements when creating source files. See Filer#createSourceFile(CharSequence, Element...) documentation. When using JavaPoet you should specify originating elements using TypeSpec.Builder#addOriginatingElement(Element) method. I can't spot a single call to this method in this repo, which can lead to missing files during build, some generated files not being rebuild when needed and other problems.

Another problem I spot is you declare your annotation processor both using AutoService library and META-INF.services file, which is kinda strange because AutoService is generating this file for you.

Also you can optimize incap support by splitting MvpCompiler into two - one for generating MoxyReflector (this one is aggregating) and one for other stuff (isolating).

I did so in my PR with incap support to original repo. I can work on migrating my changes into this repo, but I don't want to do so while processor tests are broken. I created another issue about it - #23.

[kapt] NoClassDefFoundError after updating to 2.0.1

After updating library version to 2.0.1 we will (or can) get compilation error:

[kapt] An exception occurred: java.lang.NoClassDefFoundError: com/google/common/base/Preconditions
  at moxy.compiler.Util.equalsBy(Util.java:229)
  at moxy.compiler.viewstate.ViewMethod.equals(ViewMethod.java:144)
  at java.util.ArrayList.indexOf(ArrayList.java:321)
  at java.util.ArrayList.contains(ArrayList.java:304)
  at moxy.compiler.viewstate.ViewInterfaceProcessor.getMethods(ViewInterfaceProcessor.java:187)
  at moxy.compiler.viewstate.ViewInterfaceProcessor.iterateInterfaces(ViewInterfaceProcessor.java:257)
  at moxy.compiler.viewstate.ViewInterfaceProcessor.process(ViewInterfaceProcessor.java:87)
  at moxy.compiler.viewstate.ViewInterfaceProcessor.process(ViewInterfaceProcessor.java:29)
  at moxy.compiler.MvpCompiler.generateCode(MvpCompiler.kt:172)
  at moxy.compiler.MvpCompiler.throwableProcess(MvpCompiler.kt:108)
  at moxy.compiler.MvpCompiler.process(MvpCompiler.kt:66)
  at 

As we can se from the logs, moxy-compiler uses Guava dependency, Preconditions class is used in Util.equalsBy() method (Util.java:229) But for some reasons, this dependency can't be resolved in consumer application.

Dependencies used:

implementation "com.github.moxy-community:moxy:2.0.1"
implementation "com.github.moxy-community:moxy-androidx:2.0.1"
kapt "com.github.moxy-community:moxy-compiler:2.0.1"

Possible workaround is to add Guava to your project:

implementation "com.google.guava:guava:23.0"

After this, if you have no any other Guava dependencies in your project, everything will works fine, BUT if you are using some libraries that have internal dependency to some other version of Guava you can get something like this (or similar):

Cause 1: java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.lang.RuntimeException: Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules guava-23.0.jar (com.google.guava:guava:23.0) and listenablefuture-1.0.jar (com.google.guava:listenablefuture:1.0)

In this case you will need to resolve conflicts between different Guava versions.

IllegalArgumentException: couldn't make a guess for View$$State

After migrating from old repo 1.0.13 to Moxy Community 1.0.13 (also tried 2.0-alpha06) and using android-x libraries my project stopped compiling with error:

error: Moxy compilation failed; see the compiler error output for details (java.lang.IllegalArgumentException: couldn't make a guess for com.DyDo.yury.rollertutor.app.feature.learn.mvp.view.SaveTrickView$$State)

After deleting these classes the problem still persisted with other View classes.

Here is gist with problem classes and gradle files: https://gist.github.com/23092eafcc06d53285392367be31a8f1

I also tried replacing kapt with annotationProcessor in "com.github.moxy-community:moxy-compiler:$moxyVersion". It solved the build problem, but now it seems like new presenters are not getting generated.

Moxy 2.1.0 tries to generate ViewState for BasePresenter

I have such base presenter:

import moxy.MvpPresenter
import moxy.MvpView

open class BasePresenter<View: MvpView>: MvpPresenter<View>() {

    protected val disposables = CompositeDisposable()

    override fun onDestroy() {
        disposables.clear()
    }

}

When moxy tries to generate ViewStates it also tries to generate ViewState for BasePresenter.
Which leads it to such error:

java.lang.IllegalArgumentException: View "View" for ru.yandex.eda.core.utils.moxy.BasePresenter cannot be found
	at moxy.compiler.viewstateprovider.InjectViewStateProcessor.getViewStateClassName(InjectViewStateProcessor.kt:35)
	at moxy.compiler.viewstateprovider.InjectViewStateProcessor.process(InjectViewStateProcessor.kt:25)
	at moxy.compiler.viewstateprovider.InjectViewStateProcessor.process(InjectViewStateProcessor.kt:20)
	at moxy.compiler.MvpCompiler.generateCode(MvpCompiler.kt:172)
	at moxy.compiler.MvpCompiler.processInjectors(MvpCompiler.kt:154)
	at moxy.compiler.MvpCompiler.throwableProcess(MvpCompiler.kt:100)
	at moxy.compiler.MvpCompiler.process(MvpCompiler.kt:66)
	at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.process(incrementalProcessors.kt)
	at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.process(annotationProcessing.kt:147)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1068)
	at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing(annotationProcessing.kt:79)
	at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing$default(annotationProcessing.kt:35)
Note: Moxy compilation has failed. Could you copy stack trace above and write us (or open an issue on Github)?[WARN] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.squareup.moshi.kotlin.codegen.JsonClassCodegenProcessor (NON_INCREMENTAL), javaslang.match.PatternsProcessor (NON_INCREMENTAL).
	at org.jetbrains.kotlin.kapt3.base.Kapt.kapt(Kapt.kt:45)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.jetbrains.kotlin.gradle.internal.KaptExecution.run(KaptWithoutKotlincTask.kt:146)
	at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:41)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.execute(NoIsolationWorkerFactory.java:58)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
	at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:51)
	at org.gradle.workers.internal.DefaultWorkerExecutor$1.call(DefaultWorkerExecutor.java:107)
	at org.gradle.workers.internal.DefaultWorkerExecutor$1.call(DefaultWorkerExecutor.java:101)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:748)

Androidx

It does not support androidx.appcompat.app.AppCompatActivity

Add onPresenterDestroy() lifecycle hook

In MvpFragment there is complex logic to decide whether getMvpDelegate().onDestroy() should be called.

Consider adding some lifecycle method to notify extending class when getMvpDelegate().onDestroy() was called. It can be used to close DI or some other scope, which has lifespan same as presenter.

When using getViewState in a for loop, the view method only works once

Here is an example code where I call the view method through a presenter in a loop:

Presenter:

  for (int i = 0; i<exampleList.size(); i++) {
        String title = exampleList.get(i).getTitle;
        int id = exampleList.get(i).getID;
        getViewState().addMenuItem(title, id);
        Log.d(TAG, "addMenuItem: "+i);
            }

Activity:

@Override
 public void addMenuItem(String title, int id) {
     navMenuCategory.add(R.id.menu_category, id, Menu.FIRST, title);
 }

For example: I need to add 5 menu items. Of these, only the last fifth item will be added. Default Strategy (Tried to add SingleStrategy, AddToEndSingleStrategy, did not help).

Is there a solution to this problem, or is it better for me to transfer the list of necessary items in view and cyclically add them to the menu?

Thank you in advance for your response!

MvpAppCompatActivity not found

import moxy.MvpAppCompatActivity; shows red line showing cannot resolve symbol MvpAppCompatActivity.

build.gradle(app)

implementation "com.github.moxy-community:moxy:2.1.2"
annotationProcessor "com.github.moxy-community:moxy-compiler:2.1.1"

Annotation processors must be explicitly declared now

I'm trying to migrate from 1.5.6 to community version 2.0.1
Dependencies in the build.gradle:

    implementation "com.github.moxy-community:moxy:$moxyVersion"
    implementation "com.github.moxy-community:moxy-androidx:$moxyVersion"
    implementation "com.github.moxy-community:moxy-ktx:$moxyVersion"
    kapt "com.github.moxy-community:moxy-compiler:$moxyVersion"

And i got an error:

> Configure project :app
WARNING: API 'variant.getMergeResources()' is obsolete and has been replaced with 'variant.getMergeResourcesProvider()'.
It will be removed at the end of 2019.
For more information, see https://d.android.com/r/tools/task-configuration-avoidance.
To determine what is calling variant.getMergeResources(), use -Pandroid.debug.obsoleteApi=true on the command line to display more information.

> Task :app:preBuild UP-TO-DATE
> Task :app:preStagingDebugBuild UP-TO-DATE
> Task :app:compileStagingDebugAidl NO-SOURCE
> Task :app:checkStagingDebugManifest UP-TO-DATE
> Task :app:generateStagingDebugBuildConfig UP-TO-DATE
> Task :app:compileStagingDebugRenderscript NO-SOURCE
> Task :app:mainApkListPersistenceStagingDebug UP-TO-DATE
> Task :app:mergeStagingDebugShaders UP-TO-DATE
> Task :app:compileStagingDebugShaders UP-TO-DATE
> Task :app:generateStagingDebugAssets UP-TO-DATE
> Task :app:mergeStagingDebugAssets UP-TO-DATE
> Task :app:processStagingDebugGoogleServices UP-TO-DATE
> Task :app:createStagingDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:generateStagingDebugResValues
> Task :app:processStagingDebugManifest UP-TO-DATE
> Task :app:fabricGenerateResourcesStagingDebug
> Task :app:generateStagingDebugResources

> Transform artifact moxy.jar (com.github.moxy-community.moxy:moxy:2.0.2) with DexingNoClasspathTransform
AGPBI: {"kind":"error","text":"Invoke-customs are only supported starting with Android O (--min-api 26)","sources":[{}],"tool":"D8"}

> Task :app:mergeStagingDebugResources

> Transform artifact javapoet.jar (com.squareup:javapoet:1.11.1) with DexingNoClasspathTransform
AGPBI: {"kind":"error","text":"Invoke-customs are only supported starting with Android O (--min-api 26)","sources":[{}],"tool":"D8"}

> Task :app:processStagingDebugResources

> Transform artifact guava.jar (com.google.guava:guava:23.0) with DexingNoClasspathTransform
AGPBI: {"kind":"error","text":"Default interface methods are only supported starting with Android N (--min-api 24): boolean com.google.common.base.Predicate.test(java.lang.Object)","sources":[{}],"tool":"D8"}

> Task :app:kaptGenerateStubsStagingDebugKotlin

> Task :app:kaptStagingDebugKotlin
w: [kapt] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.squareup.moshi.kotlin.codegen.JsonClassCodegenProcessor (NON_INCREMENTAL), javaslang.match.PatternsProcessor (NON_INCREMENTAL).

> Task :app:compileStagingDebugKotlin
w: /home/dda/StudioProjects/hypermarket-android-app/app/src/main/kotlin/ru/ng/base/moxy/BasePresenter.kt: (15, 47): Parameter 'parameters' is never used
w: /home/dda/StudioProjects/hypermarket-android-app/app/src/main/kotlin/ru/ng/util/Logging.kt: (154, 21): Name shadowed: thr
w: /home/dda/StudioProjects/hypermarket-android-app/app/src/main/kotlin/ru/ng/util/Logging.kt: (171, 21): Name shadowed: thr
w: /home/dda/StudioProjects/hypermarket-android-app/app/src/main/kotlin/ru/ng/util/Logging.kt: (188, 21): Name shadowed: thr
w: /home/dda/StudioProjects/hypermarket-android-app/app/src/main/kotlin/ru/ng/util/Logging.kt: (205, 21): Name shadowed: thr
w: /home/dda/StudioProjects/hypermarket-android-app/app/src/main/kotlin/ru/ng/util/Logging.kt: (222, 21): Name shadowed: thr

> Task :app:javaPreCompileStagingDebug FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:javaPreCompileStagingDebug'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > Annotation processors must be explicitly declared now.  The following dependencies on the compile classpath are found to contain annotation processor.  Please add them to the annotationProcessor configuration.
       - moxy-compiler-2.0.2.jar (com.github.moxy-community.moxy:moxy-compiler:2.0.2)
     Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior.  Note that this option is deprecated and will be removed in the future.
     See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.4.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 33s
17 actionable tasks: 8 executed, 9 up-to-date

Research replacing dependencies from local to remote for publication

Сейчас зависимости между модулями мокси стоят как project() для публикации их надо заменять на зависимости из мавена. нужно придумать способ, чтобы во время публикации не делать это руками

Moxy not works with inline types in interface parameters

Example code

import kotlin.time.Duration
import moxy.MvpView

interface FooView : MvpView {
    fun bar(duration: Duration)
}

Error

error: FooView$$State is not abstract and does not override abstract method bar-LRDsOJo(double) in FooView
public class FooView$$State extends MvpViewState<FooView> implements FooView {
       ^

Variable name clasing in ViewState

When view contains "views" variable name, like in method in my case:

@StateStrategyType(SkipStrategy::class)
fun showToolbarInfo(views: Long, date: Long)

Java compiler throws an error inside generated ViewState class because it can't decide what type to use in iterator. In my case:

@Override
public void showToolbarInfo(long views, long date) {
	ShowToolbarInfoCommand showToolbarInfoCommand = new ShowToolbarInfoCommand(views, date);
	viewCommands.beforeApply(showToolbarInfoCommand);

	if (hasNotView()) {
		return;
	}

	for (ArticleView view : views) {
		view.showToolbarInfo(views, date);
	}

	viewCommands.afterApply(showToolbarInfoCommand);
}

I understand that simply renaming the variable in my View interface would solve the problem, but still.

I am using Moxy 2.0.1

Split repository

We have the organization for moxy at the moment. Maybe we can remove template for android studio and intelij from the repository. And create another repository for the templates.

P.S. I have a idea for create one more template for the library.

moxy-compiler tests are broken in develop branch

Hi! I was trying to make PR similar to what I made to original moxy repo (this one) but I find out compiler tests are completely broken in develop branch. So to properly test my changes I have to fix existing tests first, which to be honest I don't have time and motivation to do. Is there some branch with fixed tests I don't know about or tests are broken and everyone ok about it?

moxyPresenter delegate name(ex-tag) linking is not working

Hello! Moxy 2.0.2 user here.

We've encountered an issue, when there is adapter with bunch of fragments and their named presenters, names are not working. No linking by name is happening, and it's more like linking by index, so when one of items is deleted and adapter's notifyDataSetChanged() called - all items to the right of deleted one are messed up with wrong presenters.

That's happening in case of using moxyPresenter(name) delegate only though, and it's working totally fine with annotations @InjectPresenter + @ProvidePresenter + @ProvidePresenterTag.

Thanks!

Problem with redirecting from push notification

In my project i use Moxy Library to inject View State.When my app is killed and i got a message as a push notification and want to click and redirect to senders page, Viewstate is null and cant implement my Activity's method(which opens the chat)),Can you give me solution to this problem?
Thanks

NoSuchMethodError after migrating to moxy community

After migrating from old repo (https://github.com/Arello-Mobile/Moxy) 1.5.3 version to moxy community 1.0.13 im getting build errors from javapoet:
e: [kapt] An exception occurred: java.lang.BootstrapMethodError: java.lang.NoSuchMethodError: com.squareup.javapoet.ParameterSpec.get(Ljavax/lang/model/element/VariableElement;)Lcom/squareup/javapoet/ParameterSpec;

The dependency setup is correct, and i'm getting same error in appCompat and androidX versions:

    moxyVersion = '1.5.3'
"com.arello-mobile:moxy:$moxyVersion",
"com.arello-mobile:moxy-compiler:$moxyVersion",
"com.arello-mobile:moxy-app-compat:$moxyVersion",

Also had same problem with combination of old version from arello + module from schoolhelper (https://github.com/schoolhelper/MoxyX), and same is true when all modules are from schoolhelper.

Full stacktrace below:

e: [kapt] An exception occurred: java.lang.BootstrapMethodError: java.lang.NoSuchMethodError: com.squareup.javapoet.ParameterSpec.get(Ljavax/lang/model/element/VariableElement;)Lcom/squareup/javapoet/ParameterSpec;
	at com.arellomobile.mvp.compiler.viewstate.ViewMethod.<init>(ViewMethod.java:40)
	at com.arellomobile.mvp.compiler.viewstate.ViewInterfaceProcessor.getMethods(ViewInterfaceProcessor.java:129)
	at com.arellomobile.mvp.compiler.viewstate.ViewInterfaceProcessor.process(ViewInterfaceProcessor.java:56)
	at com.arellomobile.mvp.compiler.viewstate.ViewInterfaceProcessor.process(ViewInterfaceProcessor.java:36)
	at com.arellomobile.mvp.compiler.MvpCompiler.generateCode(MvpCompiler.java:216)
	at com.arellomobile.mvp.compiler.MvpCompiler.throwableProcess(MvpCompiler.java:139)
	at com.arellomobile.mvp.compiler.MvpCompiler.process(MvpCompiler.java:111)
	at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.process(annotationProcessing.kt:106)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
	at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1068)
	at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing(annotationProcessing.kt:58)
	at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing$default(annotationProcessing.kt:31)
	at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.runAnnotationProcessing(Kapt3Extension.kt:223)
	at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.analysisCompleted(Kapt3Extension.kt:187)
	at org.jetbrains.kotlin.kapt3.ClasspathBasedKapt3Extension.analysisCompleted(Kapt3Extension.kt:98)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM$analyzeFilesWithJavaIntegration$2.invoke(TopDownAnalyzerFacadeForJVM.kt:96)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:106)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:82)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:384)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:70)
	at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:107)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:375)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:123)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:159)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:57)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:96)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:52)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:93)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$1$1$2.invoke(CompileServiceImpl.kt:442)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$1$1$2.invoke(CompileServiceImpl.kt:102)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:1013)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:102)
	at org.jetbrains.kotlin.daemon.common.DummyProfiler.withMeasure(PerfUtils.kt:137)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.checkedCompile(CompileServiceImpl.kt:1055)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.doCompile(CompileServiceImpl.kt:1012)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:441)
	at sun.reflect.GeneratedMethodAccessor104.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:346)
	at sun.rmi.transport.Transport$1.run(Transport.java:200)
	at sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoSuchMethodError: com.squareup.javapoet.ParameterSpec.get(Ljavax/lang/model/element/VariableElement;)Lcom/squareup/javapoet/ParameterSpec;
	at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
	at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:975)
	at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1000)
	at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1394)
	at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1750)
	at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:477)
	... 57 more

Behaviour on different Android versions

Hi! I have various presenter behaviour on different Android versions because of MvpDelegate.onDetach call in MvpAppCompatFragment.onSaveInstanceState



MvpAppCompatFragment lifecycle callbacks after request permission:



Android 6 (API 23):
…

onResume

(request permission)

onPause

onSavedInstanceState



Android 10 (API 29):


onResume

(request permission)

onPause


In my app i use custom MvpFragment(with the same logic) and plan to remove onDetach from onSaveInstanceState. I see no reason not to do it. Could you explain please why this logic was added or provide cases of possible memory leaks?

Add Java 8 compatibility info

There are some requirements for the build script, that should be defined in the project, using Moxy. Users must define Java 8 compatibility info for the R8.

android{
    compileOptions {
        targetCompatibility 1.8
        sourceCompatibility 1.8
    }

Please add info about this to Wiki or README.md

Is it realistic to use moxy for viewholders in recyclerview? Have a sample?

I have created a presenter for the view holder to display it correctly according to the data. But now, unfortunately, you have to do it in the classic way without moxy. I tried to make the view holder as a custom view, but it has no lifecycle. Tell me, is there a way to work with recyclerview adapter and its view holders using moxy? :)

TaskAdapter:

public class TaskAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int VIEW_TASK = 1;
    private static final int VIEW_HEADER_COMPLETED_TASKS = 2;
    private static final int VIEW_COMPLETED_TASKS = 3;
    private final TasksPresenter presenter;
    private List<Task> taskList;

    public TaskAdapter(TasksPresenter presenter) {
        this.presenter = presenter;
        taskList = new ArrayList<>();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v;
        switch (viewType) {
            case VIEW_TASK:
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
                return new TaskViewHolder(v);
		...
    }

    @Override
    public int getItemViewType(int position) {
        if (taskList.get(position).isComplete())
            return VIEW_COMPLETED_TASKS;
		...
    }

    @Override
    public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
        if (getItemViewType(position) == VIEW_TASK) {
            TaskViewHolder taskViewHolder = (TaskViewHolder) holder;
            taskViewHolder.bind(taskList.get(position), presenter);
        }
		...
    }

    public List<Task> getData() {
        return taskList;
    }

    public void setData(List<Task> taskList) {
        this.taskList = taskList;
    }

    @Override
    public int getItemCount() {
        return taskList.size();
    }
		...
}

TaskViewHolder:

public class TaskViewHolder extends RecyclerView.ViewHolder implements TaskMvpHolder.view {

    private final DateDisplay display;
    private TaskViewHolderPresenter holderPresenter;
    private LinearLayout dateHolder;
    private TextView titleHolder, textHolder, dateTextHolder;
    private CheckBox checkBoxHolder;
    private ImageView priorityHolder;

    public TaskViewHolder(View itemView) {
        super(itemView);
        holderPresenter = new TaskViewHolderPresenter(this);
   		...
    }

    public void bind(@NonNull Task task, TasksMvp.presenter tasksPresenter) {
        holderPresenter.onBind(task);

        itemView.setOnClickListener(v -> tasksPresenter.onTaskItemClick(task.getId()));

        checkBoxHolder.setOnClickListener(v -> tasksPresenter.onTaskItemCheckboxClick(task.getId()));
    }

    @Override
    public void showTitle(String title) {
        titleHolder.setText(title);
    }

    @Override
    public void showText(String text) {
        textHolder.setText(text);
        textHolder.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideText() {
        textHolder.setVisibility(View.GONE);
    }

    @Override
    public void showReminder(Calendar cal, String time) {
		...
        dateTextHolder.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideReminder() {
        dateHolder.setVisibility(View.GONE);
    }

    @Override
    public void showPriority() {
		...
    }

    @Override
    public void hidePriority() {
        priorityHolder.setVisibility(View.GONE);
    }
}

TaskViewHolderPresenter:

public class TaskViewHolderPresenter implements TaskMvpHolder.presenter {

    TaskMvpHolder.view view;

    public TaskViewHolderPresenter(TaskMvpHolder.view view) {
        this.view = view;
    }

    @Override
    public void onBind(@NonNull Task task) {
        String title = task.getTitle();
        String text = task.getText();
        String date = task.getDate();
        String time = task.getTime();
        Calendar cal = Calendar.getInstance();
        int priority = task.getPriority();

        view.showTitle(title);

        if (!text.equals("")) {
            view.showText(text);
        } else view.hideText();

        if (!date.equals("")) {
		...
            view.showReminder(cal, time);
        } else view.hideReminder();

        if (priority != 0) {
            view.showPriority();
        } else view.hidePriority();
    }
}

I hope the example is readable :)

Генерируется несколько ViewState в многомодульном проекте (версии 2.1.1 и 2.1.2)

После обновления до 2.1.2 билд многомодульного проекта фейлится с ошибкой:

Type moxy.MvpView$$State is defined multiple times

На 2.1.1 то же самое, а вот 2.0.2 билдится нормально.

Полный лог билда: https://travis-ci.com/github/RankoR/android-smart-rate/jobs/341635309

Проект, на котором воспроизводил: https://github.com/RankoR/android-smart-rate/

Save Spinner state

Hello.
I have a problem: when I rotate screen, spinner resets its selection. How can I save spinner state with Moxy?
SelectQuizFragment

@JvmField
@InjectPresenter
var presenter: SelectQuizPresenter? = null

private lateinit var categorySpinner: Spinner
private lateinit var difficultySpinner: Spinner
private lateinit var adapter: SpinnerAdapter
private lateinit var startButton: Button

lateinit var navController: NavController
private var categoryId: Int = -1

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val root = inflater.inflate(R.layout.fragment_select_quiz, container, false)

    categorySpinner = root.findViewById(R.id.category_spinner)
    difficultySpinner = root.findViewById(R.id.difficulty_spinner)
    startButton = root.findViewById(R.id.start_button)

    startButton.setOnClickListener {
        val bundle = bundleOf(
            "categoryId" to categoryId,
            "difficulty" to difficultySpinner.selectedItem.toString().toLowerCase()
        )
        navController.navigate(
            R.id.action_navigation_select_quiz_to_quizFragment,
            bundle
        )
    }
    makeSpinnerClickable()
    return root
}

private fun makeSpinnerClickable() {
    categorySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
        override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
            categoryId = adapter.getItem(p2).id
        }

        override fun onNothingSelected(p0: AdapterView<*>?) { }
    }
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    navController = Navigation.findNavController(view)
}

override fun setCategorySpinnerItems(categories: List<Category>) {
    adapter = SpinnerAdapter(activity!!.applicationContext, categories)
    categorySpinner.adapter = adapter
}

SelectQuizView
interface SelectQuizView : MvpView { @StateStrategyType(value = AddToEndSingleStrategy::class) fun setCategorySpinnerItems(categories: List<Category>) }

SelectQuizPresenter

override fun onFirstViewAttach() {
    super.onFirstViewAttach()
    CoroutineScope(IO).launch {
        getCategories()
    }
}

private suspend fun getCategories() {
    val quizApi = QuizApi()
    val currentCategoryResult = quizApi.getCategories()
    withContext(Main){
        viewState.setCategorySpinnerItems(currentCategoryResult.category)
    }
}

Parametrized AddToEndSingleByTagStateStrategy ?

Hi all,
please give me idea how to implement the following use case using any built-in Strategy or how to achieve this using a kind of Custom Strategy:

Say, I have a GoogleMap, and I want add a group of Markers in special color. Also, I can add more groups of Markers in any another color. Then, I want to hide some group of markers in special color, leave another groups untouched. Let's say we have:

interface MapView : MvpView {
  // to show group of markers in special color
  @StateStrategyType(AddToEndStrategy::class)  <- let's start with this strategy type
  fun showMarkers(markers: List<Marker>, int color)

  // to hide group of markers 
  @StateStrategyType(AddToEndStrategy::class)  <- let's start with this strategy type
  fun hideMarkers(markers: List<Marker>)
}

then on Presenter side we have something like:

class MapPresenter {
...
viewState.showMarkers(listOfRedMarkers, RED)

viewState.showMarkers(listOfGreenMarkers, GREEN)
viewState.hideMarkers(listOfGreenMarkers, GREEN)

 <here we show/hide green markers many-many times, say 1 hundred times - because we can, right?>

viewState.showMarkers(listOfGreenMarkers, GREEN)
viewState.hideMarkers(listOfGreenMarkers, GREEN)

viewState.showMarkers(listOfBlueMarkers, BLUE)
...
}

then - device Orientation Changes.

Expected result of restoring view state commands called:

viewState.showMarkers(listOfRedMarkers, RED)
viewState.showMarkers(listOfBlueMarkers, BLUE)

and that's it.
Note: no any call to show/hide green markers (1 hundred times, right?)

How can I achieve this? Which Strategy to use ? Thanks in advance!

Add javadoc and sources for android-artifacts

Android Gradle plugin 3.6-alpha07 and higher includes support for the Maven Publish Gradle plugin by creating a component for each build variant artifact that you use to customize a publication to a Maven repository.

ProGuard\R8 obfuscates classes with Injected View States

Hi!
After removing InjectViewState annotations -keepnames @moxy.InjectViewState class * rule become useless
Consequently ViewStateLocator can not get ViewState and bind presenter.
Need to replace with something like -keepnames class * extends moxy.MvpPresenter

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.