> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/xamarin/basic-operations/basics/tools/intercept-annotation-handling.md).

# Customize annotation handling

Learn how to customize user actions in Xamarin.Android with the Tools library. Intercept annotation events and get notified after an action. Enhance your app's functionality now! The Apryse Xamarin SD

{% hint style="info" %}
**This tutorial only applies to Xamarin.Android. See Xamarin.iOS equivalent here .**
{% endhint %}

By default, the Tools library will respond to user actions such as a click event on a link or form widget, or a property change event on a selected annotation. It is possible to customize such behavior before it is executed within the Tools library, as well as getting notified of an action after it has happened.

## Intercept annotation events

If you would like to customize the behavior executed in response to user actions **prior** to it being handled by the Tools library, it is possible to do so with the `BasicAnnotationListener.onInterceptAnnotationHandling(Annot, Bundle, int)` API. If this method returns true, then it will intercept executing the default logic.

In `BasicAnnotationListener.onInterceptAnnotationHandling(Annot, Bundle, int)`, the second parameter, `Bundle`, contains the name of the action intercepted by this function, as well as some extra information. You can get the name of the intercepted action by calling `bundle.getString(Tool.METHOD_FROM)`:

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
public boolean onInterceptAnnotationHandling(Annot annot, Bundle extra, ToolMode toolMode) {
    if (extra != null && extra.containsKey(Tool.METHOD_FROM)) {
        String methodCalling = extra.getString(Tool.METHOD_FROM);
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
override fun onInterceptAnnotationHandling(annot: Annot?, extra: Bundle?, toolMode: ToolMode): Boolean {
    if (extra != null && extra.containsKey(Tool.METHOD_FROM)) {
        val methodCalling = extra.getString(Tool.METHOD_FROM)
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

To get extra information, first obtain the information keys by calling `bundle.getStringArray(Tool.KEYS)`:

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
String[] paramKeys = extra.getStringArray(Tool.KEYS)
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
val paramKeys = extra!!.getStringArray(Tool.KEYS)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Then, you can get the information values by looping on each key obtained above:

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
public boolean onInterceptAnnotationHandling(Annot annot, Bundle extra, ToolMode toolMode) {
    if (extra != null && extra.containsKey(Tool.KEYS)) {
        String[] paramKeys = extra.getStringArray(Tool.KEYS);
        if (paramKeys != null) {
            for (String key : paramKeys) {
                // Gets the information value
                Object param = extra.get(key);
            }
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
override fun onInterceptAnnotationHandling(annot: Annot?, extra: Bundle?, toolMode: ToolMode): Boolean {
    if (extra != null && extra.containsKey(Tool.KEYS)) {
        val paramKeys = extra.getStringArray(Tool.KEYS)
        if (paramKeys != null) {
            for (key in paramKeys) {
                // Gets the information value
                val param = extra.get(key)
            }
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Common use cases

#### Links

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
mToolManager.setBasicAnnotationListener(new ToolManager.BasicAnnotationListener() {
    @Override
    public boolean onInterceptAnnotationHandling(Annot annot, Bundle extra, ToolMode toolMode) {
        try {
            // Intercept clicking link annotation by return true
            if (annot.getType() == Annot.e_Link) {
                Log.d("InterceptAnnot", "handling link annotation");
                return true;
            }
        } catch (PDFNetException e) {
            e.printStackTrace();
        }
        // return false so the other events can continue executing
        return false;
    }
});
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
mToolManager.setBasicAnnotationListener(object : ToolManager.BasicAnnotationListener {
    override fun onInterceptAnnotationHandling(annot: Annot?, extra: Bundle?, toolMode: ToolMode?): Boolean {
        try {
            // Intercept clicking link annotation by return true
            if (annot!!.type == Annot.e_Link) {
                Log.d("InterceptAnnot", "handling link annotation")
                return true
            }
        } catch (e: PDFNetException) {
            e.printStackTrace()
        }
        // return false so the other events can continue executing
        return false
    }
})
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Form widgets

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
mToolManager.setBasicAnnotationListener(new ToolManager.BasicAnnotationListener() {
    @Override
    public boolean onInterceptAnnotationHandling(Annot annot, Bundle extra, ToolMode toolMode) {
        try {
            // Intercept clicking widget annotation by return true
            if (annot.getType() == Annot.e_Widget) {
                Log.d("InterceptAnnot", "handling widget annotation");
                return true;
            }
        } catch (PDFNetException e) {
            e.printStackTrace();
        }
        // return false so the other events can continue executing
        return false;
    }
});
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
mToolManager.setBasicAnnotationListener(object : ToolManager.BasicAnnotationListener {
    override fun onInterceptAnnotationHandling(annot: Annot?, extra: Bundle?, toolMode: ToolMode?): Boolean {
        try {
            // Intercept clicking widget annotation by return true
            if (annot!!.type == Annot.e_Widget) {
                Log.d("InterceptAnnot", "handling widget annotation")
                return true
            }
        } catch (e: PDFNetException) {
            e.printStackTrace()
        }
        // return false so the other events can continue executing
        return false
    }
})
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Full sample code

For example, the following code demonstrates several scenarios and how you can get notified **before** each: clicking on a radio button, changing the annotation opacity, and clicking on a link. See the comments in the code for details.

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
mToolManager.setBasicAnnotationListener(new ToolManager.BasicAnnotationListener() {
    @Override
    public boolean onInterceptAnnotationHandling(Annot annot, Bundle extra, ToolMode toolMode) {
        /*
            For example, when a radio button is clicked, this code will print:
            calling method: handleWidget
            property: OTHER
        */
        if (extra != null && extra.containsKey(Tool.METHOD_FROM)) {
            String methodCalling = extra.getString(Tool.METHOD_FROM);
            Log.d("InterceptAnnot", "calling method " + methodCalling);
            AnnotationProperty.Property property = AnnotationProperty.getProperty(methodCalling);
            Log.d("InterceptAnnot", "property: " + property);
        }
        /*
            For example, when annotation opacity is about to be changed to 50%, this code will print:
            calling method: editOpacity
            property: OPACITY
            key: opacity
            value: 0.5
        */
        if (extra != null && extra.containsKey(Tool.KEYS)) {
            String[] paramKeys = extra.getStringArray(Tool.KEYS);
            if (paramKeys != null) {
                for (String key : paramKeys) {
                    Object param = extra.get(key);
                    Log.d("InterceptAnnot", "key: " + key);
                    Log.d("InterceptAnnot", "value: " + param.toString());
                }
            }
        }
        try {
            // Intercept radio button from clicking by return true
            if(annot.getType() == Annot.e_Widget) {
                Widget w = new Widget(annot);
                Field field = w.getField();
                if (field != null && field.isValid() && field.getType() == Field.e_radio) {
                    return true;
                }
            }
            // Intercept clicking link annotation by return true
            if (annot.getType() == Annot.e_Link) {
                Log.d("InterceptAnnot", "handling link annotation");
            }
        } catch (PDFNetException e) {
            e.printStackTrace();
        }
        // return false so the other events can continue executing
        return false;
    }
});
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
mToolManager.setBasicAnnotationListener(object : ToolManager.BasicAnnotationListener {
    override fun onInterceptAnnotationHandling(annot: Annot?, extra: Bundle?, toolMode: ToolMode?): Boolean {
        /*
      For example, when a radio button is clicked, this code will print:
      calling method: handleWidget
      property: OTHER
      */
        if (extra != null && extra.containsKey(Tool.METHOD_FROM)) {
            val methodCalling = extra.getString(Tool.METHOD_FROM)
            Log.d("InterceptAnnot", "calling method " + methodCalling)
            val property = AnnotationProperty.getProperty(methodCalling)
            Log.d("InterceptAnnot", "property: " + property)
        }
        /*
      For example, when annotation opacity is about to be changed to 50%, this code will print:
      calling method: editOpacity
      property: OPACITY
      key: opacity
      value: 0.5
      */
        if (extra != null && extra.containsKey(Tool.KEYS)) {
            val paramKeys = extra.getStringArray(Tool.KEYS)
            if (paramKeys != null) {
                for (key in paramKeys) {
                    val param = extra.get(key)
                    Log.d("InterceptAnnot", "key: " + key)
                    Log.d("InterceptAnnot", "value: " + param.toString())
                }
            }
        }
        try {
            // Intercept radio button from clicking by return true
            if (annot!!.type == Annot.e_Widget) {
                val w = Widget(annot)
                val field = w.getField()
                if (field != null && field.isValid() && field.getType() === Field.e_radio) {
                    return true
                }
            }
            // Intercept clicking link annotation by return true
            if (annot!!.type == Annot.e_Link) {
                Log.d("InterceptAnnot", "handling link annotation")
            }
        } catch (e: PDFNetException) {
            e.printStackTrace()
        }
        // return false so the other events can continue executing
        return false
    }
})
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Get notified after an action

If you simply want to get notified **after** the action has happened, you can do so via the `AnnotationModificationListener.onAnnotationsModified(Map<Annot, Integer>)` API. This event callback is raised when an annotation change has occurred. For example, in order to get notified when the value of a checkbox has been changed and when annotation opacity has been changed, do the following:

{% tabs %}
{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
ToolManager.AnnotationModificationListener annotationModificationListener = new ToolManager.AnnotationModificationListener() {
    @Override
    public void onAnnotationsModified(Map<Annot, Integer> annots, Bundle extra) {
        /*
            For example, when a checkbox's value has been changed, this code will print:
            calling method: handleWidget
            property: OTHER
        */
        if (extra != null && extra.containsKey(Tool.METHOD_FROM)) {
            String methodCalling = extra.getString(Tool.METHOD_FROM);
            Log.d("AnnotationsModified", "calling method " + methodCalling);
            AnnotationProperty.Property property = AnnotationProperty.getProperty(methodCalling);
            Log.d("AnnotationsModified", "property: " + property);
        }
        /*
            For example, when annotation opacity is changed to 50%, this code will print:
            calling method: editOpacity
            property: OPACITY
            key: opacity
            value: 0.5
        */
        if (extra != null && extra.containsKey(Tool.KEYS)) {
            String[] paramKeys = extra.getStringArray(Tool.KEYS);
            if (paramKeys != null) {
                for (String key : paramKeys) {
                    Object param = extra.get(key);
                    Log.d("AnnotationsModified", "key: " + key);
                    Log.d("AnnotationsModified", "value: " + param.toString());
                }
            }
        }
    }
};
mToolManager.addAnnotationModificationListener(annotationModificationListener);
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
val annotationModificationListener: ToolManager.AnnotationModificationListener = object : ToolManager.AnnotationModificationListener {
  override fun onAnnotationsModified(annots: MutableMap<Annot, Int>?, extra: Bundle?) {
    /*
      For example, when a checkbox's value has been changed, this code will print:
      calling method: handleWidget
      property: OTHER
      */
    if (extra != null && extra.containsKey(Tool.METHOD_FROM)) {
      val methodCalling = extra.getString(Tool.METHOD_FROM)
      Log.d("AnnotationsModified", "calling method " + methodCalling)
      val property = AnnotationProperty.getProperty(methodCalling)
      Log.d("AnnotationsModified", "property: " + property)
    }
    /*
      For example, when annotation opacity is changed to 50%, this code will print:
      calling method: editOpacity
      property: OPACITY
      key: opacity
      value: 0.5
      */
    if (extra != null && extra.containsKey(Tool.KEYS)) {
      val paramKeys = extra.getStringArray(Tool.KEYS)
      if (paramKeys != null) {
        for (key in paramKeys) {
          val param = extra.get(key)
          Log.d("AnnotationsModified", "key: " + key)
          Log.d("AnnotationsModified", "value: " + param.toString())
        }
      }
    }
  }
}
mToolManager.addAnnotationModificationListener(annotationModificationListener)
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
mToolManager.AnnotationsModified += (sender, e) =>
{
    var extra = e.Extra;
    /*
        For example, when checkbox value has been changed, this code will print:
        calling method: handleWidget
        property: OTHER
    */
    if (extra != null && extra.ContainsKey(Tool.MethodFrom))
    {
        String methodCalling = extra.GetString(Tool.MethodFrom);
        Console.WriteLine("AnnotationsModified calling method " + methodCalling);
        var property = pdftron.PDF.Model.AnnotationProperty.GetProperty(methodCalling);
        Console.WriteLine("AnnotationsModified property: " + property);
    }

    /*
        For example, when changed annotation opacity to 50%
        calling method: editOpacity
        property: OPACITY
        key: opacity
        value: 0.5
    */
    if (extra != null && extra.ContainsKey(Tool.Keys))
    {
        String[] paramKeys = extra.GetStringArray(Tool.Keys);
        if (paramKeys != null)
        {
            foreach (String key in paramKeys)
            {
                Object param = extra.Get(key);
                Console.WriteLine("AnnotationsModified key: " + key);
                Console.WriteLine("AnnotationsModified value: " + param.ToString());
            }
        }
    }

    // Do something with the annots
    foreach (var item in e.Annots)
    {
        var nativeAnnot = item.Key;
        var annot = TypeConvertHelper.ConvAnnotToManaged(nativeAnnot);
        if (annot != null && annot.IsValid())
        {
            Annot.Type type = annot.GetType();
            Console.WriteLine("AnnotationsModified: type: " + type);
        }
    }
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

We are always interested in expanding the customization options through APIs, and will be adding more options in the future. If you would like to suggest changes, please don't hesitate to [get in touch](https://apryse.com/form/feature-request).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/xamarin/basic-operations/basics/tools/intercept-annotation-handling.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
