Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save jeryini/f5d672b86735b946f2da07739ce49ae8 to your computer and use it in GitHub Desktop.

Select an option

Save jeryini/f5d672b86735b946f2da07739ce49ae8 to your computer and use it in GitHub Desktop.
Mobile-UXSDK-Beta-Android Mavic Mini YUV data example
/*
* Copyright (c) 2018-2020 DJI
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package com.dji.ux.beta.sample;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import java.nio.ByteBuffer;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import dji.common.airlink.PhysicalSource;
import dji.midware.usb.P3.UsbAccessoryService;
import dji.sdk.codec.DJICodecManager;
import dji.thirdparty.io.reactivex.android.schedulers.AndroidSchedulers;
import dji.thirdparty.io.reactivex.disposables.CompositeDisposable;
import dji.ux.beta.cameracore.widget.fpvinteraction.FPVInteractionWidget;
import dji.ux.beta.core.extension.ViewExtensions;
import dji.ux.beta.core.panelwidget.systemstatus.SystemStatusListPanelWidget;
import dji.ux.beta.core.panelwidget.topbar.TopBarPanelWidget;
import dji.ux.beta.core.util.SettingDefinitions;
import dji.ux.beta.core.widget.fpv.FPVWidget;
import dji.ux.beta.core.widget.gpssignal.GPSSignalWidget;
import dji.ux.beta.core.widget.simulator.SimulatorIndicatorWidget;
import dji.ux.beta.core.widget.systemstatus.SystemStatusWidget;
import dji.ux.beta.hardwareaccessory.widget.rtk.RTKWidget;
import dji.ux.beta.map.widget.map.MapWidget;
import dji.ux.beta.training.widget.simulatorcontrol.SimulatorControlWidget;
/**
* Displays a sample layout of widgets similar to that of the various DJI apps.
*/
public class DefaultLayoutActivity extends AppCompatActivity {
//region Fields
private final static String TAG = "DefaultLayoutActivity";
@BindView(R.id.widget_fpv)
protected FPVWidget fpvWidget;
@BindView(R.id.widget_fpv_interaction)
protected FPVInteractionWidget fpvInteractionWidget;
@BindView(R.id.widget_map)
protected MapWidget mapWidget;
@BindView(R.id.widget_secondary_fpv)
protected FPVWidget secondaryFPVWidget;
@BindView(R.id.root_view)
protected ConstraintLayout parentView;
@BindView(R.id.widget_panel_system_status_list)
protected SystemStatusListPanelWidget systemStatusListPanelWidget;
@BindView(R.id.widget_rtk)
protected RTKWidget rtkWidget;
@BindView(R.id.widget_simulator_control)
protected SimulatorControlWidget simulatorControlWidget;
private boolean isMapMini = true;
private int widgetHeight;
private int widgetWidth;
private int widgetMargin;
private int deviceWidth;
private int deviceHeight;
private CompositeDisposable compositeDisposable;
//endregion
//region Lifecycle
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_default_layout);
widgetHeight = (int) getResources().getDimension(R.dimen.mini_map_height);
widgetWidth = (int) getResources().getDimension(R.dimen.mini_map_width);
widgetMargin = (int) getResources().getDimension(R.dimen.mini_map_margin);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
deviceHeight = displayMetrics.heightPixels;
deviceWidth = displayMetrics.widthPixels;
ButterKnife.bind(this);
mapWidget.initAMap(map -> map.setOnMapClickListener(latLng -> onViewClick(mapWidget)));
mapWidget.getUserAccountLoginWidget().setVisibility(View.GONE);
mapWidget.onCreate(savedInstanceState);
// Setup top bar state callbacks
TopBarPanelWidget topBarPanel = findViewById(R.id.panel_top_bar);
SystemStatusWidget systemStatusWidget = topBarPanel.getSystemStatusWidget();
if (systemStatusWidget != null) {
systemStatusWidget.setStateChangeCallback(findViewById(R.id.widget_panel_system_status_list));
}
SimulatorIndicatorWidget simulatorIndicatorWidget = topBarPanel.getSimulatorIndicatorWidget();
if (simulatorIndicatorWidget != null) {
simulatorIndicatorWidget.setStateChangeCallback(findViewById(R.id.widget_simulator_control));
}
GPSSignalWidget gpsSignalWidget = topBarPanel.getGPSSignalWidget();
if (gpsSignalWidget != null) {
gpsSignalWidget.setStateChangeCallback(findViewById(R.id.widget_rtk));
}
DefaultLayoutActivity activity = this;
fpvWidget.setCodecManagerCallback(new FPVWidget.CodecManagerCallback() {
@Override
public void onCodecManagerChanged(@Nullable DJICodecManager codecManager) {
Log.d(TAG, "Codec manager changed");
codecManager = new DJICodecManager(activity, null, 1280, 720, UsbAccessoryService.VideoStreamSource.Camera);
codecManager.enabledYuvData(true);
Log.d(TAG, "YUV data enabled");
codecManager.setYuvDataCallback(new DJICodecManager.YuvDataCallback() {
@Override
public void onYuvDataReceived(MediaFormat mediaFormat, ByteBuffer yuvFrame, int dataSize, int width, int height) {
Log.d(TAG, "New YUV data received");
}
});
}
});
}
@Override
protected void onDestroy() {
mapWidget.onDestroy();
super.onDestroy();
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
mapWidget.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapWidget.onLowMemory();
}
@Override
protected void onResume() {
super.onResume();
mapWidget.onResume();
compositeDisposable = new CompositeDisposable();
compositeDisposable.add(secondaryFPVWidget.getCameraName()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::updateSecondaryVideoVisibility));
compositeDisposable.add(systemStatusListPanelWidget.closeButtonPressed()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(pressed -> {
if (pressed) {
ViewExtensions.hide(systemStatusListPanelWidget);
}
}));
compositeDisposable.add(rtkWidget.getWidgetStateUpdate()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(rtkWidgetState -> {
if (rtkWidgetState instanceof RTKWidget.RTKWidgetState.VisibilityUpdate) {
if (((RTKWidget.RTKWidgetState.VisibilityUpdate) rtkWidgetState).isVisible()) {
hideOtherPanels(rtkWidget);
}
}
}));
compositeDisposable.add(simulatorControlWidget.getUIStateUpdates()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(simulatorControlWidgetState -> {
if (simulatorControlWidgetState instanceof SimulatorControlWidget.SimulatorControlWidgetUIUpdate.VisibilityToggled) {
if (((SimulatorControlWidget.SimulatorControlWidgetUIUpdate.VisibilityToggled) simulatorControlWidgetState).isVisible()) {
hideOtherPanels(simulatorControlWidget);
}
}
}));
}
@Override
protected void onPause() {
if (compositeDisposable != null) {
compositeDisposable.dispose();
compositeDisposable = null;
}
mapWidget.onPause();
super.onPause();
}
//endregion
//region Utils
private void hideOtherPanels(@Nullable View widget) {
View[] panels = {
rtkWidget,
simulatorControlWidget
};
for (View panel : panels) {
if (widget != panel) {
panel.setVisibility(View.GONE);
}
}
}
/**
* Handles a click event on the FPV widget
*/
@OnClick(R.id.widget_fpv)
public void onFPVClick() {
onViewClick(fpvWidget);
}
/**
* Handles a click event on the secondary FPV widget
*/
@OnClick(R.id.widget_secondary_fpv)
public void onSecondaryFPVClick() {
swapVideoSource();
}
/**
* Swaps the FPV and Map Widgets.
*
* @param view The thumbnail view that was clicked.
*/
private void onViewClick(View view) {
if (view == fpvWidget && !isMapMini) {
//reorder widgets
parentView.removeView(fpvWidget);
parentView.addView(fpvWidget, 0);
//resize widgets
resizeViews(fpvWidget, mapWidget);
//enable interaction on FPV
fpvInteractionWidget.setInteractionEnabled(true);
//disable user login widget on map
mapWidget.getUserAccountLoginWidget().setVisibility(View.GONE);
isMapMini = true;
} else if (view == mapWidget && isMapMini) {
//reorder widgets
parentView.removeView(fpvWidget);
parentView.addView(fpvWidget, parentView.indexOfChild(mapWidget) + 1);
//resize widgets
resizeViews(mapWidget, fpvWidget);
//disable interaction on FPV
fpvInteractionWidget.setInteractionEnabled(false);
//enable user login widget on map
mapWidget.getUserAccountLoginWidget().setVisibility(View.VISIBLE);
isMapMini = false;
}
}
/**
* Helper method to resize the FPV and Map Widgets.
*
* @param viewToEnlarge The view that needs to be enlarged to full screen.
* @param viewToShrink The view that needs to be shrunk to a thumbnail.
*/
private void resizeViews(View viewToEnlarge, View viewToShrink) {
//enlarge first widget
ResizeAnimation enlargeAnimation = new ResizeAnimation(viewToEnlarge, widgetWidth, widgetHeight, deviceWidth, deviceHeight, 0);
viewToEnlarge.startAnimation(enlargeAnimation);
//shrink second widget
ResizeAnimation shrinkAnimation = new ResizeAnimation(viewToShrink, deviceWidth, deviceHeight, widgetWidth, widgetHeight, widgetMargin);
viewToShrink.startAnimation(shrinkAnimation);
}
/**
* Swap the video sources of the FPV and secondary FPV widgets.
*/
private void swapVideoSource() {
if (secondaryFPVWidget.getVideoSource() == SettingDefinitions.VideoSource.SECONDARY) {
fpvWidget.setVideoSource(SettingDefinitions.VideoSource.SECONDARY);
secondaryFPVWidget.setVideoSource(SettingDefinitions.VideoSource.PRIMARY);
} else {
fpvWidget.setVideoSource(SettingDefinitions.VideoSource.PRIMARY);
secondaryFPVWidget.setVideoSource(SettingDefinitions.VideoSource.SECONDARY);
}
}
/**
* Hide the secondary FPV widget when there is no secondary camera.
*
* @param cameraName The name of the secondary camera.
*/
private void updateSecondaryVideoVisibility(String cameraName) {
if (cameraName.equals(PhysicalSource.UNKNOWN.name())) {
secondaryFPVWidget.setVisibility(View.GONE);
} else {
secondaryFPVWidget.setVisibility(View.VISIBLE);
}
}
//endregion
//region classes
/**
* Animation to change the size of a view.
*/
private static class ResizeAnimation extends Animation {
private static final int DURATION = 300;
private View view;
private int toHeight;
private int fromHeight;
private int toWidth;
private int fromWidth;
private int margin;
private ResizeAnimation(View v, int fromWidth, int fromHeight, int toWidth, int toHeight, int margin) {
this.toHeight = toHeight;
this.toWidth = toWidth;
this.fromHeight = fromHeight;
this.fromWidth = fromWidth;
view = v;
this.margin = margin;
setDuration(DURATION);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float height = (toHeight - fromHeight) * interpolatedTime + fromHeight;
float width = (toWidth - fromWidth) * interpolatedTime + fromWidth;
ConstraintLayout.LayoutParams p = (ConstraintLayout.LayoutParams) view.getLayoutParams();
p.height = (int) height;
p.width = (int) width;
p.rightMargin = margin;
p.bottomMargin = margin;
view.requestLayout();
}
}
//endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment