-
Notifications
You must be signed in to change notification settings - Fork 55
UX SDK Hooks
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.
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.
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.
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.
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.
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)
}
}
})
}
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);
}
}
}));
}
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.
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.
}
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.
}
The hooks can also be leveraged to add logs and/or analytics to the usage of the widgets.
- The logging use of the storage by observing the SD Card Status List Item Model State.
- 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:
- All widgets with widget models will provide Model Hooks.
- Only the widgets which have significant UI changes or interactions will provide UI Hooks.
DJI UX SDK Version 5 Beta 5
UX SDK 5.0 Overview
Core Module
Camera Core Module
Visual Cameras Module
- Camera Config