Skip to content
This repository has been archived by the owner on Feb 26, 2023. It is now read-only.

Implement a generalized way to handle Event Binding #2219

Open
smaugho opened this issue Feb 24, 2019 · 2 comments
Open

Implement a generalized way to handle Event Binding #2219

smaugho opened this issue Feb 24, 2019 · 2 comments

Comments

@smaugho
Copy link
Contributor

smaugho commented Feb 24, 2019

Event binding in AA right now is limited to some specific View Listeners, let's say @Click, @OnLongClick, or similar.

Even if all those annotations are quire similar, it is mandatory to create a new annotation handler for each one of them, and basically change AA (or create a plugin) to support others.

But creating a generalization of this mechanism is not that complicated. I would like to suggest a solution which could serve to generalize the implementation of these methods.

Firstly, a generic @ViewEvent annotation should be created, which looks as follow:

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface ViewEvent {

    /**
     * The R.id.* fields which refer to the Views.
     *
     * @return the ids of the Views
     */
    int[] value() default {};

    /**
     * The resource names as strings which refer to the Views.
     *
     * @return the resource names of the Views
     */
    String[] resName() default {};

     /**
     * Class which contains the listener provided
     *
     * @return the listener holder class name
     */
    String listenerHolderView();

     /**
     * Listener which will be used to bind the annotated method.
     *
     * @return the listener method name
     */
    String listener();

     /**
     * Suffixes which could be used to name the methods
     *
     * @return valid suffix for the methods
     */   
    String[] validSuffix() default {};

}

The idea of creating this @ViewEvent annotation is not to be used directly, but there is no issue if the programmer wants to use it directly. But the biggest advantage is to be able to create a new annotation from it, which will be processed by AA. I'll add example of two annotations, @Click and @LongClick:

@ViewEvent(listenerHolderView="android.view.View", listener = "setOnClickListener", validSuffix = {"Click", "Clicked", "Tap", "Tapped"})
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Click {

    /**
     * The R.id.* fields which refer to the Views.
     *
     * @return the ids of the Views
     */
    int[] value() default ResId.DEFAULT_VALUE;

    /**
     * The resource names as strings which refer to the Views.
     *
     * @return the resource names of the Views
     */
    String[] resName() default {};

}

@ViewEvent(listenerHolderView="android.view.View", listener = "setOnLongClickListener -> return true;", validSuffix = {"LongClick", "LongClicked", "LongTap", "LongTapped"})
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface LongClick {

    /**
     * The R.id.* fields which refer to the Views.
     *
     * @return the ids of the Views
     */
    int[] value() default ResId.DEFAULT_VALUE;

    /**
     * The resource names as strings which refer to the Views.
     *
     * @return the resource names of the Views
     */
    String[] resName() default {};

}

Now, the validation and the processing of the annotation should be done by the holder annotation, so any ComponentWithViewSupport annotation, will check its method to look for any annotation which is annotated with @ViewEvent, and it will execute:

  • validations based on the parameters of the provided listener
  • bind the view based on the provided listener.

The process is not really that complicated, since given the listenerHolderView you can access to the method via its TypeElement, detect the parameters and validate them. The interface for it is also fully generated.

I see many advantages of this approach:

  • It permits developers to add other listeners if they need to, since the annotations are easily created.
  • It is fully compatible with the existing annotations, AA will simply provide some implementations for all the current annotations provided by the framework (@Click, @LongClick, ....)

I was already "playing" with some implementation for this.

@dodgex
Copy link
Member

dodgex commented Feb 24, 2019

I kind of like the idea. But i think we should do two PRs/Changes for it.

First: Fully support Meta-Annotations to allow custom annotations to include multiple AA annotations.

@Click
@Background
public @interface BackgroundClick{}

or even more powerfull having something like in spring framework with @AliasFor to allow properties of the custom annotations be passed to the annotation it provides. I'm pretty sure that the annotation handling stuff from spring can be used in any application even those who are not actually use spring. but I'm not sure if it works for annotations processors :(

and then with that support your suggestion shouldn't be a big deal anymore :)

@smaugho
Copy link
Contributor Author

smaugho commented Feb 24, 2019

@dodgex , this multi-annotation you are suggesting is definitely a generalization of my suggestion, since it will permit to make a simple annotation (@ViewEvent), and @Click or any other are simply "implementations" of that one, with specific parameters.

This could be done, but we'll have to change a little how annotations are handled, specifically, the Decorating Annotations. To be able to do this, the Generating Annotations probably should "check all the class" looking for other annotations (since we cannot request to process on your example for instance the @BackgroundClick, since this could be declared even out of AA, so there is no way to know it at priory). So basically the implementation of all the decorating annotations should lead with this.

A possible implementation would be:

  • On the ModelValidator, only the Generating Handlers are validated.
  • Per each TypeElement annotated with one Generating annotations (@EBean, @EActivity, ...), each field, method and method param should be scanned
  • If they are annotated, that annotation should be checked against existing decorating handlers.
  • If any of those handlers is found, then the validate is called on them with the element (class, field, method, param).

The ModelProcessor implementation would be similar, after generateElements() concludes, each of those elements is scanned per annotations following same algorithm, and the respective decoratingHandler.process() is called per each of them.

I personally don't see the implementation that difficult. Each annotation should be simply marked to be able to annotate another annotation. Not sure if a first implementation would need the @AliasFor, but it can be definitely done.

@WonderCsabo what do you think if this feature?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants