# How to use

### Overview

The wrapper classes are placed in the `org.apertusvr` package. For each ApertusVR entity type there's a corresponding Java wrapper class. Also there are a wrapper classes for the manager classes (e.g.  `ape::EventManager`, `ape::SceneManager`)and `ape::Event` and there are several supporting classes (e.g. `apeColor`, `apeVector3`). To be able to cast the interfaces, we ha a builder Java interface (`apeBuilder`) too.

### Event connection

Just like using the C++ interface, the user can connect to various events (fired by ApertusVR) from Java code. To do that, we need to implement the `apeEventCallback` function:

```java
class MyEventCallback implements apeEventCallback {
    @Override
    public void onEvent(apeEvent event) {
        // do sth with event
    }
}
```

We also have to create an instance of the `MyEventCallback` class, e.g.

```java
MyEventCallback eventCallback = new MyEventCallback();
```

After instantiation, the connection works simply with the usage of `apeManager`'s `connectEvent(...)` function. E.g., to be able to listen `NODE` events, just type:

```java
apeEventManager.connectEvent(apeEvent.Group.NODE, eventCallback);
```

To get an insight of what actually happens here, let us discuss the details! When the user connects an event, the `apeEventManager` (which is a static class) puts this `eventCallback` into a map:

```java
private static Map<apeEvent.Group, LinkedList<apeEventCallback>> mEventMap
```

The app developer also creates a `FrameCallback`  class, which implements Android's built in `Choreographer.FrameCallback`. Like in the following code block:

```java
class FrameCallback implements Choreographer.FrameCallback {
        @Override
        public void doFrame(long frameTimeNanos) {
            choreographer.postFrameCallback(this);
            
            if (apeSystem.isRunning()) {
                ApertusJNI.processEventDoubleQueue();
            }
        }
    }
```

This will parse all the events fired in the C++ side, during the last frame. The ApertusJNI member function called `processEventDoubleQueue()` is a JNI-function, which calls back to Java with `apeEventManager`'s `fireEvent()` function, which will call the `onEvent(apeEvent)` function of our `apeEventCallback` implementation, if its instance is connected to the event's group (so the implementation object can be found under the `apeEvent.Group.NODE` key in the map).

![](/files/-MEXN73sQQwihBMzTxz9)

### Entity wrappers

As it was written before, for each entity class in ApertusVR we have a corresponding Java class. The member functions and the class hierarchies on the Java side are the same, as they are on the C++ side. Therefore the API does not differ much, but there are tiny differences between the two. We will go through everything you need, to use ApertusVR entities from Java.

#### Getting access to entities

In C++, knowing the entity's name, we ask the `ape::ISceneManager` to give us a weak pointer. Then we use the `lock()` function to access it. After calling `lock()`, we can also cast the resulted `shared_ptr` to the proper type (`ape::Entity::Type`), if we know what the entity's type should be.\
When using ApertusVR from Java, we just use the interface's constructor to get access to an entity, then we check with the `isValid()` function, that we can actually lock it in C++.

E.g. assume, that we want to access an `apeFileGeometry` entity we know about, with the name of `"FooBar"`, and our goal is to query its `OwnerID`. The procedure goes something like on the following code example:

```java
String fooBarName = "FooBar";
apeFileGeometry fooBarGeometry = new apeFileGeometry(fooBarName);

if (fooBarGeometry.isValid()) {
    String ownerID = fooBarGeometry.getOwnerID();
}
```

The following figure summarizes the procedure executed in the background:

![](/files/-MEbRleCQjR-y7O8W5lU)

This is the way in 90% of the cases. However, what if we don't know already the type of the entity, and only its name is given. In this case, we can use the `apeSceneManager` interface:

```java
String fooBarName = "FooBar";
apeEntity fooBarEntity = apeSceneManager.getEntity(fooBarName);

if (fooBarEntity != null && fooBarEntity.isValid()) {
    apeEntity.Type fooBarType = fooBarEntity.getType();
    
    if (fooBarType == apeEntity.Type.GEOMETRY_FILE) {
        apeFileGeometry fooBarGeometry = new apeFileGeometry(fooBarName);
        // ...
    }
}
```

#### Creating an entity

The next question is, how can we create entities in Java? Easy. Just use the `apeSceneManager` interface!:

```java
String fooBarName = "FooBar";
apeEntity.Type fooBarType = apeEntity.Type.GEOMETRY_FILE;
boolean fooBarIsReplicate = false;
String fooBarOwner = "FooBarOwner";

apeEntity fooBarEntity = apeSceneManager.createEntity(
    fooBarName, fooBarType, fooBarIsReplicate, fooBarOwner);
```

&#x20;Okay, then how do we cast it to our needs? We wanted `apeFileGeometry` interface, not `apeEntity`.\
This is where the `apeBuilder` interface come in! The `apeEntity` abstract class has a member function called `cast`:

```java
public <apeType extends apeEntity> apeType cast(apeBuilder<apeType> builder)
```

Providing a proper builder class (which implements the `apeBuilder` interface), we can cast our `apeEntity` to any given type extended from it. In the example, we just type:

```java
apeFileGeometry fooBarGeometry =  Objects.requireNonNull(
        apeSceneManager.createEntity(
            fooBarName,
            fooBarType,
            fooBarIsReplicate,
            fooBarOwner))
            .cast(new apeFileGeometry.FileGeometryBuilder());
```

Or an alternative way is to instance an other `apeFileGeometry` entity  with the `apeEntity`'s name you got from `apeSceneManager` (like you saw it in the former sub-subsection).

#### Deleting entities

Deleting an entity in Java is the same as in C++, an example is presented below:

```java
String entityName = "FooBar";
apeSceneManager.deleteEntity(entityName);
```

### Writing an application

Now you know how to query events and handle them with entity usage, we can discuss how you can set up ApertusVR for your android application with the support of the JNI-plugin.

On Android, we build our applications from [Activity classes](https://developer.android.com/guide/components/activities/intro-activities), and each activity has its own *lifecycle*. To learn about activity lifecycles, see the [Android guide on activity lifecycles](https://developer.android.com/guide/components/activities/activity-lifecycle). In code it appears as five function we have to override:

```java
protected void onCreate(Bundle savedInstanceState) {
    // ...
}

protected void onStart() {
    // ...
}

protected void onResume() {
    // ...
}

protected void onPause() {
    // ...
}

protected void onDestroy() {
    // ...
}
```

When the activity is launched (and the `onCreate(...)` function is called) by Android , we have to do three things:

1. Get an instance from [Android choreographer](https://developer.android.com/reference/android/view/Choreographer), and store it for later usages.
2. &#x20;Start the ApertusVR system and optionally connect to a room.
3. Initialize the plug-ins for our application (more about it in the Plugins section).

The 1st is done by typing `choreographer = Choreographer.getInstance()`. Not a big deal. The 2nd needs further explanation, and we will go through the 3rd in the next section.

#### Start ApertusVR

Before doing anything with ApertusVR, we have to call its C++ start method in the `ape::System`namespace:

```cpp
void Start(const char* configFolderPath, bool isBlocking, std::function<void()> userThreadFunction = std::function<void()>());
```

When we do that, we have to tell ApertusVR where it can find the **apeCore.json** file (`configFolderPath`), which contains necessary informations. On Android it is stored in the assets folder of the given application. Therefore, when calling this method through the JNI-interface and the `apeSystem` wrapper class, we type:

```java
String configFolderPath = /* some path in the assets folder */;

if (!apeSystem.isRunning()) {
    apeSystem.start(configFolderPath, getAssets());
}
```

As you see, we have to provide the [Android AssetManager](https://developer.android.com/reference/android/content/res/AssetManager) (`getAssets()`) for the start function. This will call the JNI-function `startApertusVR(...),` which will call the actual `ape::System::Start(...)`function.&#x20;

{% hint style="info" %}
For reading the asset folder from C++, the Android build has a shared library called apeAAssetOpen. The apeCoreConfig module, uses this shared library to open the asset folder. The JNI-method `startApertusVR(...)` also initializes apeAAssetOpen library.
{% endhint %}

#### Connect to room

ApertusVR users on Android-Java can set the room they want to connect pre-runtime in the `apeCore.json`, or runtime with the help of the `apeSceneNetwork` wrapper. So basically everything goes like in the C++ case:

```java
/* start ApertusVR */

String roomName = /* existing room name */
apeSceneNetwork.connectToRoom(roomName);
```

### Plugins

In the ApertusVR C++ API plug-ins are implementations of the `ape::IPlugin` interface. Developing on Android and Java, they have a different form. Our Java plug-ins are actually Android implementations of the Android`LifecycleObserver` interface.

For being able to partition our application, Android provides us an interface called [`LifecycleObserver`](https://developer.android.com/reference/android/arch/lifecycle/LifecycleObserver). Classes implementing this interface (called *lifecycle-aware components*) can be attached to the activity's lifecycle as something which constantly observes its lifecycle, and respond to the events occouring during the activity's lifetime. To learn more about lifecycle-aware components, again please read the [Android guide on the given topic](https://developer.android.com/topic/libraries/architecture/lifecycle).

As activities can select their lifecycle-aware components, it works kind of like a plug-in mechanism. Therefore the ApertusVR Android API contains an interface called `apePlugin`, which only presents a `LifecycleObserver` interface as it was a plug-in(ish thing):

{% code title="apePlugin.java" %}

```java
package org.apertusvr;

import /* ... */

public interface apePlugin extends LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreate();

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart();

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume();

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause();

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy();
}
```

{% endcode %}

So, in order to create a plug-in which works on Android and can respond to ApertusVR events from Java code, we need to implement this interface (or equivalently implement the "raw" `LifecycleObserver` interface):

```java
public final class apeExamplePlugin implements apePlugin {

    public apeExamplePlugin(Context context, Lifecycle lifecycle, /* ... */) {
        // constructing the plugin
    }
    
    @Override
    void onCreate() {
        // ...
    }

    @Override
    void onStart() {
        // ...
    }

    @Override
    void onResume() {
        // ...
    }

    @Override
    void onPause() {
        // ...
    }

    @Override
    void onDestroy() {
        // ...
    }

}
```

Then in our activity, when we want to start to use this example plug-in (e.g., when Android creates the activity), we create an instance of the plug-in, then attach it to the activity's lifecycle:

```java
public class ExampleActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // ...
     
        /* instance the example plug-in */
        
        apeExamplePlugin examplePlugin = new apeExamplePlugin(
                this, getLifecycle(), /* ... */);
        
        getLifecycle().addObserver(examplePlugin);
    }
}
```

{% hint style="warning" %}
Not every `Activity` subclass supports lifecycle-aware components. `AppCompatActivity` specially supports it.
{% endhint %}

{% hint style="info" %}
You can use configuration files for the plug-in if needed. Place the configuration file in the application's asset folder, then just query the path for the configuration file from the `apeCoreCinfig.getConfigFolderPath()`.&#x20;
{% endhint %}


---

# Agent Instructions: 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:

```
GET https://apertus.gitbook.io/vr/plugins-android/jni-plugin-android/how-to-use.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
