Skip to content

UX SDK Hooks

Siddharth Utgikar edited this page Dec 22, 2020 · 1 revision

UX SDK 5 has been written to allow developers to use UX SDK widgets, write their own widgets, or modify and extend the widgets supplied in UX SDK 5. Widgets can be customized to match a developers application appearance and color scheme.

Hooks and What They Are

In addition to the flexibility provided, UX SDK also provides a new feature called Hooks for receiving event and widget status changes without having to write or override existing widgets. These hooks are sent to listeners who register for them whenever a hook event happens.

Hooks allow your application a consistent way to handle response to changes without implementing detailed code to access and monitor underlying states. If you are collecting analytics information on certain areas hooks can also supply those changes for recording.

General hook format and data containment

Hooks consist of the following parts: a sender, a receiver object, and the hook object with data itself. Hooks are defined as a key and one or more pieces of data containing relevant information, packaged into an instance of a hook class.

On Android, each widget maintains an RxJava PublishProcessor which holds the widget state and is updated with the changes. The widget exposes a public API which provides a Flowable based on BackpressureBuffer strategy. Observing this flowable will provide the latest state updates.

Standard Hook Class Naming Conventions

There are two types of Hooks:

  • Model Hooks This type of hook provides an insight into the data received by the widget from the widget model. All updates from the SDK keys and UXSDK keys that the widget receives can be observed in the form of composite states. The model hook states that a widget provides are represented by a sealed class ModelState inside the widget.
sealed class ModelState {
   data class ProductConnected(val isProductConnected: Boolean) : ModelState()
}

The user can subscribe to this using fun getWidgetStateUpdate(): Flowable<ModelState>.

  • UI Hooks This type of hook provides information regarding changes to the user interface and user interaction. The UI hook states that a widget provides are represented by a sealed class UIState inside the widget.
sealed class UIState {
   object WidgetClicked : UIState()
   data class DialogDisplayed(val info: DialogType) : UIState()
}

The user can subscribe to this using fun getUIStateUpdate(): Flowable<UIState>.

Below are some examples of utilizing the hooks.

Example 1

Let us consider the DefaultLayoutActivity which provides an example of the manual flight user interface. This activity hosts multiple panel widgets like System Status List, RTK Widget, Simulator Control Widget and so on. Having all these widgets open at the same time can make the experience very cluttered. The experience can be made better by leveraging the UI Hooks from the widgets.

Kotlin

open class DefaultLayoutActivity: AppCompatActivity {
    // Other activity code.. 

   // Helper method to hide all widgets except the value passed
   private fun hideOtherWidgets(widget: View) {
       val widgets = arrayOf<View>(rtkWidget, simulatorControlWidget)
                    .filter { it != widget }
                    .map { it.visibility = View.GONE }
   }


// Add this to close all other widgets when RTK Widget is opened
compositeDisposable.add(rtkWidget.getUIStateUpdates()
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe { uiState: RTKWidget.UIState? ->
             if (uiState is RTKWidget.UIState.VisibilityUpdated) {
                 if (uiState.isVisible) {
                     hideOtherWidgets(rtkWidget)
                 }
             }
         })

// Add this to close all other widgets when Simulator Control Widget is opened
compositeDisposable.add(simulatorControlWidget.getUIStateUpdates()
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe { simulatorControlWidgetState: SimulatorControlWidget.UIState? ->
            if (simulatorControlWidgetState is SimulatorControlWidget.UIState.VisibilityUpdated) {
                 if (simulatorControlWidgetState.isVisible) {
                     hideOtherWidgets(simulatorControlWidget)
                 }
             }
          })

}

Java

public class DefaultLayoutActivity extends AppCompatActivity {
    // Other activity code.. 

   // Helper method to hide all widgets except the value passed
   private void hideOtherWidgets(@Nullable View widget) {
        View[] widgets = {
                rtkWidget,
                simulatorControlWidget
        };

        for (View view : widgets) {
            if (widget != view) {
                view.setVisibility(View.GONE);
            }
        }
    }


// Add this in onResume to close all other widgets when RTK Widget is opened
compositeDisposable.add(rtkWidget.getUIStateUpdates()
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe(uiState -> {
             if (uiState instanceof RTKWidget.UIState.VisibilityUpdated) {
                if (((RTKWidget.UIState.VisibilityUpdated) uiState).isVisible()) {
                    hideOtherWidgets(rtkWidget);
                 }
             }
         }));



// Add this in onResume to close all other widgets when Simulator Control Widget is opened
compositeDisposable.add(simulatorControlWidget.getUIStateUpdates()
         .observeOn(AndroidSchedulers.mainThread())
         .subscribe(simulatorControlWidgetState -> {
             if (simulatorControlWidgetState instanceof SimulatorControlWidget.UIState.VisibilityUpdated) {
                 if (((SimulatorControlWidget.UIState.VisibilityUpdated) simulatorControlWidgetState).isVisible()) {
                     hideOtherWidgets(simulatorControlWidget);
                 }
             }
         }));

}

Example 2

Let us take an example of Vision Widget. The Vision Widget shows the current state of the vision system. The various statuses are indicated using a set of icons. By just observing this widget the pilot can understand that the omni directional sensors used by the drone are currently disabled if they see .

However, in some scenarios it might be of critical importance that the pilot should not fly the drone with the obstacle avoidance disabled. In such a case we can build on top of the widget using the Model Hooks provided by the Vision Widget.

Kotlin

val visionWidget: VisionWidget = findViewById(R.id.vision_widget)
// Add this to listen to the vision state updates
compositeDisposable.add(visionWidget.getWidgetStateUpdate()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                if( it is VisionWidget.ModelState.VisionSystemStateUpdated ){
                    if (it.visionSystemState == VisionWidgetModel.VisionSystemState.OMNI_CLOSED){
                        showAlertDialog()
                    }
                }
                   
            })

// Create dialog to remind user to enable Obstacle avoidance.
fun showAlertDialog() {
// Dialog code goes here.
}

Java

VisionWidget visionWidget = findViewById(R.id.vision_widget);
// Add this to listen to the vision state updates
compositeDisposable.add(visionWidget.getWidgetStateUpdate()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(widgetState-> {  
                    if(widgetState instanceof VisionWidget.ModelState.VisionSystemStateUpdated){
                        if(((VisionWidget.ModelState.VisionSystemStateUpdated) widgetState).getVisionSystemState()== VisionWidgetModel.VisionSystemState.OMNI_CLOSED){
                            showAlertDialog()
                        }
                    }
            }));

// Create dialog to remind user to enable Obstacle avoidance.
private void showAlertDialog() {
// Dialog code goes here.
}

Example 3

The hooks can also be leveraged to add logs and/or analytics to the usage of the widgets.

  1. The logging use of the storage by observing the SD Card Status List Item Model State.
  2. Logging the use of Take Off Widget can give insight into the pilot's preference for the widget against taking off using the remote controller.

Note:

  1. All widgets with widget models will provide Model Hooks.
  2. Only the widgets which have significant UI changes or interactions will provide UI Hooks.
Clone this wiki locally