package org.droidplanner.android.fragments.control;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.gms.analytics.HitBuilders;
import com.o3dr.android.client.Drone;
import com.o3dr.android.client.apis.ControlApi;
import com.o3dr.android.client.apis.FollowApi;
import com.o3dr.android.client.apis.VehicleApi;
import com.o3dr.services.android.lib.drone.attribute.AttributeEvent;
import com.o3dr.services.android.lib.drone.attribute.AttributeEventExtra;
import com.o3dr.services.android.lib.drone.attribute.AttributeType;
import com.o3dr.services.android.lib.drone.property.State;
import com.o3dr.services.android.lib.drone.property.VehicleMode;
import com.o3dr.services.android.lib.gcs.follow.FollowState;
import com.o3dr.services.android.lib.model.SimpleCommandListener;
import org.droidplanner.android.R;
import org.droidplanner.android.activities.helpers.SuperUI;
import org.droidplanner.android.dialogs.SlideToUnlockDialog;
import org.droidplanner.android.dialogs.SupportYesNoDialog;
import org.droidplanner.android.dialogs.SupportYesNoWithPrefsDialog;
import org.droidplanner.android.proxy.mission.MissionProxy;
import org.droidplanner.android.utils.analytics.GAUtils;
import org.droidplanner.android.utils.prefs.DroidPlannerPrefs;
/**
* Provide functionality for flight action button specific to copters.
*/
public class CopterFlightControlFragment extends BaseFlightControlFragment implements SupportYesNoDialog.Listener {
private static final String ACTION_FLIGHT_ACTION_BUTTON = "Copter flight action button";
private static final String DRONIE_CREATION_DIALOG_TAG = "Confirm dronie creation";
private static final IntentFilter eventFilter = new IntentFilter();
static {
eventFilter.addAction(AttributeEvent.STATE_ARMING);
eventFilter.addAction(AttributeEvent.STATE_CONNECTED);
eventFilter.addAction(AttributeEvent.STATE_DISCONNECTED);
eventFilter.addAction(AttributeEvent.STATE_UPDATED);
eventFilter.addAction(AttributeEvent.STATE_VEHICLE_MODE);
eventFilter.addAction(AttributeEvent.FOLLOW_START);
eventFilter.addAction(AttributeEvent.FOLLOW_STOP);
eventFilter.addAction(AttributeEvent.FOLLOW_UPDATE);
eventFilter.addAction(AttributeEvent.MISSION_DRONIE_CREATED);
}
private final BroadcastReceiver eventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
switch (action) {
case AttributeEvent.STATE_ARMING:
case AttributeEvent.STATE_CONNECTED:
case AttributeEvent.STATE_DISCONNECTED:
case AttributeEvent.STATE_UPDATED:
setupButtonsByFlightState();
break;
case AttributeEvent.STATE_VEHICLE_MODE:
updateFlightModeButtons();
break;
case AttributeEvent.FOLLOW_START:
case AttributeEvent.FOLLOW_STOP:
final FollowState followState = getDrone().getAttribute(AttributeType.FOLLOW_STATE);
if (followState != null) {
String eventLabel = null;
switch (followState.getState()) {
case FollowState.STATE_START:
eventLabel = "FollowMe enabled";
break;
case FollowState.STATE_RUNNING:
eventLabel = "FollowMe running";
break;
case FollowState.STATE_END:
eventLabel = "FollowMe disabled";
break;
case FollowState.STATE_INVALID:
eventLabel = "FollowMe error: invalid state";
break;
case FollowState.STATE_DRONE_DISCONNECTED:
eventLabel = "FollowMe error: drone not connected";
break;
case FollowState.STATE_DRONE_NOT_ARMED:
eventLabel = "FollowMe error: drone not armed";
break;
}
if (eventLabel != null) {
HitBuilders.EventBuilder eventBuilder = new HitBuilders.EventBuilder()
.setCategory(GAUtils.Category.FLIGHT)
.setAction(ACTION_FLIGHT_ACTION_BUTTON)
.setLabel(eventLabel);
GAUtils.sendEvent(eventBuilder);
Toast.makeText(getActivity(), eventLabel, Toast.LENGTH_SHORT).show();
}
}
/* FALL - THROUGH */
case AttributeEvent.FOLLOW_UPDATE:
updateFlightModeButtons();
updateFollowButton();
break;
case AttributeEvent.MISSION_DRONIE_CREATED:
//Get the bearing of the dronie mission.
float bearing = intent.getFloatExtra(AttributeEventExtra.EXTRA_MISSION_DRONIE_BEARING, -1);
if (bearing >= 0) {
final FlightControlManagerFragment parent = (FlightControlManagerFragment) getParentFragment();
if (parent != null) {
parent.updateMapBearing(bearing);
}
}
break;
}
}
};
private MissionProxy missionProxy;
private View mDisconnectedButtons;
private View mDisarmedButtons;
private View mArmedButtons;
private View mInFlightButtons;
private Button followBtn;
private Button homeBtn;
private Button landBtn;
private Button pauseBtn;
private Button autoBtn;
private int orangeColor;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_copter_mission_control, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
orangeColor = getResources().getColor(R.color.orange);
mDisconnectedButtons = view.findViewById(R.id.mc_disconnected_buttons);
mDisarmedButtons = view.findViewById(R.id.mc_disarmed_buttons);
mArmedButtons = view.findViewById(R.id.mc_armed_buttons);
mInFlightButtons = view.findViewById(R.id.mc_in_flight_buttons);
final View connectBtn = view.findViewById(R.id.mc_connectBtn);
connectBtn.setOnClickListener(this);
homeBtn = (Button) view.findViewById(R.id.mc_homeBtn);
homeBtn.setOnClickListener(this);
final Button armBtn = (Button) view.findViewById(R.id.mc_armBtn);
armBtn.setOnClickListener(this);
final Button disarmBtn = (Button) view.findViewById(R.id.mc_disarmBtn);
disarmBtn.setOnClickListener(this);
landBtn = (Button) view.findViewById(R.id.mc_land);
landBtn.setOnClickListener(this);
final Button takeoffBtn = (Button) view.findViewById(R.id.mc_takeoff);
takeoffBtn.setOnClickListener(this);
pauseBtn = (Button) view.findViewById(R.id.mc_pause);
pauseBtn.setOnClickListener(this);
autoBtn = (Button) view.findViewById(R.id.mc_autoBtn);
autoBtn.setOnClickListener(this);
final Button takeoffInAuto = (Button) view.findViewById(R.id.mc_TakeoffInAutoBtn);
takeoffInAuto.setOnClickListener(this);
followBtn = (Button) view.findViewById(R.id.mc_follow);
followBtn.setOnClickListener(this);
final Button dronieBtn = (Button) view.findViewById(R.id.mc_dronieBtn);
dronieBtn.setOnClickListener(this);
}
@Override
public void onApiConnected() {
super.onApiConnected();
missionProxy = getMissionProxy();
setupButtonsByFlightState();
updateFlightModeButtons();
updateFollowButton();
getBroadcastManager().registerReceiver(eventReceiver, eventFilter);
}
@Override
public void onApiDisconnected() {
super.onApiDisconnected();
getBroadcastManager().unregisterReceiver(eventReceiver);
}
@Override
public void onClick(View v) {
HitBuilders.EventBuilder eventBuilder = new HitBuilders.EventBuilder()
.setCategory(GAUtils.Category.FLIGHT);
final Drone drone = getDrone();
switch (v.getId()) {
case R.id.mc_connectBtn:
((SuperUI) getActivity()).toggleDroneConnection();
break;
case R.id.mc_armBtn:
getArmingConfirmation();
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel("Arm");
break;
case R.id.mc_disarmBtn:
VehicleApi.getApi(drone).arm(false);
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel("Disarm");
break;
case R.id.mc_land:
VehicleApi.getApi(drone).setVehicleMode(VehicleMode.COPTER_LAND);
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel(VehicleMode
.COPTER_LAND.getLabel());
break;
case R.id.mc_takeoff:
getTakeOffConfirmation();
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel("Takeoff");
break;
case R.id.mc_homeBtn:
VehicleApi.getApi(drone).setVehicleMode(VehicleMode.COPTER_RTL);
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel(VehicleMode.COPTER_RTL
.getLabel());
break;
case R.id.mc_pause: {
final FollowState followState = drone.getAttribute(AttributeType.FOLLOW_STATE);
if (followState.isEnabled()) {
FollowApi.getApi(drone).disableFollowMe();
}
ControlApi.getApi(drone).pauseAtCurrentLocation(null);
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel("Pause");
break;
}
case R.id.mc_autoBtn:
VehicleApi.getApi(drone).setVehicleMode(VehicleMode.COPTER_AUTO);
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel(VehicleMode.COPTER_AUTO.getLabel());
break;
case R.id.mc_TakeoffInAutoBtn:
getTakeOffInAutoConfirmation();
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel(VehicleMode.COPTER_AUTO.getLabel());
break;
case R.id.mc_follow:
toggleFollowMe();
break;
case R.id.mc_dronieBtn:
getDronieConfirmation();
eventBuilder.setAction(ACTION_FLIGHT_ACTION_BUTTON).setLabel("Dronie uploaded");
break;
default:
eventBuilder = null;
break;
}
if (eventBuilder != null) {
GAUtils.sendEvent(eventBuilder);
}
}
private void getDronieConfirmation() {
SupportYesNoWithPrefsDialog ynd = SupportYesNoWithPrefsDialog.newInstance(getActivity()
.getApplicationContext(), DRONIE_CREATION_DIALOG_TAG,
getString(R.string.pref_dronie_creation_title),
getString(R.string.pref_dronie_creation_message), DroidPlannerPrefs.PREF_WARN_ON_DRONIE_CREATION, this);
if (ynd != null) {
ynd.show(getChildFragmentManager(), DRONIE_CREATION_DIALOG_TAG);
}
}
private void getTakeOffConfirmation() {
final SlideToUnlockDialog unlockDialog = SlideToUnlockDialog.newInstance("take off", new Runnable() {
@Override
public void run() {
final double takeOffAltitude = getAppPrefs().getDefaultAltitude();
ControlApi.getApi(getDrone()).takeoff(takeOffAltitude, null);
}
});
unlockDialog.show(getChildFragmentManager(), "Slide to take off");
}
private void getTakeOffInAutoConfirmation() {
final SlideToUnlockDialog unlockDialog = SlideToUnlockDialog.newInstance("take off in auto", new Runnable() {
@Override
public void run() {
final double takeOffAltitude = getAppPrefs().getDefaultAltitude();
final Drone drone = getDrone();
ControlApi.getApi(drone).takeoff(takeOffAltitude, new SimpleCommandListener() {
@Override
public void onSuccess() {
VehicleApi.getApi(drone).setVehicleMode(VehicleMode.COPTER_AUTO);
}
});
}
});
unlockDialog.show(getChildFragmentManager(), "Slide to take off in auto");
}
private void getArmingConfirmation() {
SlideToUnlockDialog unlockDialog = SlideToUnlockDialog.newInstance("arm", new Runnable() {
@Override
public void run() {
VehicleApi.getApi(getDrone()).arm(true);
}
});
unlockDialog.show(getChildFragmentManager(), "Slide To Arm");
}
private void updateFlightModeButtons() {
resetFlightModeButtons();
State droneState = getDrone().getAttribute(AttributeType.STATE);
if (droneState == null)
return;
final VehicleMode flightMode = droneState.getVehicleMode();
if (flightMode == null)
return;
switch (flightMode) {
case COPTER_AUTO:
autoBtn.setActivated(true);
break;
case COPTER_BRAKE:
pauseBtn.setActivated(true);
break;
case COPTER_RTL:
homeBtn.setActivated(true);
break;
case COPTER_LAND:
landBtn.setActivated(true);
break;
default:
break;
}
}
private void resetFlightModeButtons() {
homeBtn.setActivated(false);
landBtn.setActivated(false);
pauseBtn.setActivated(false);
autoBtn.setActivated(false);
}
private void updateFollowButton() {
FollowState followState = getDrone().getAttribute(AttributeType.FOLLOW_STATE);
if (followState == null)
return;
switch (followState.getState()) {
case FollowState.STATE_START:
followBtn.setBackgroundColor(orangeColor);
break;
case FollowState.STATE_RUNNING:
followBtn.setActivated(true);
followBtn.setBackgroundResource(R.drawable.flight_action_row_bg_selector);
break;
default:
followBtn.setActivated(false);
followBtn.setBackgroundResource(R.drawable.flight_action_row_bg_selector);
break;
}
}
private void resetButtonsContainerVisibility() {
mDisconnectedButtons.setVisibility(View.GONE);
mDisarmedButtons.setVisibility(View.GONE);
mArmedButtons.setVisibility(View.GONE);
mInFlightButtons.setVisibility(View.GONE);
}
private void setupButtonsByFlightState() {
final State droneState = getDrone().getAttribute(AttributeType.STATE);
if (droneState != null && droneState.isConnected()) {
if (droneState.isArmed()) {
if (droneState.isFlying()) {
setupButtonsForFlying();
} else {
setupButtonsForArmed();
}
} else {
setupButtonsForDisarmed();
}
} else {
setupButtonsForDisconnected();
}
}
private void setupButtonsForDisconnected() {
resetButtonsContainerVisibility();
mDisconnectedButtons.setVisibility(View.VISIBLE);
}
private void setupButtonsForDisarmed() {
resetButtonsContainerVisibility();
mDisarmedButtons.setVisibility(View.VISIBLE);
}
private void setupButtonsForArmed() {
resetButtonsContainerVisibility();
mArmedButtons.setVisibility(View.VISIBLE);
}
private void setupButtonsForFlying() {
resetButtonsContainerVisibility();
mInFlightButtons.setVisibility(View.VISIBLE);
}
@Override
public boolean isSlidingUpPanelEnabled(Drone drone) {
if (!drone.isConnected())
return false;
final State droneState = drone.getAttribute(AttributeType.STATE);
return droneState.isArmed() && droneState.isFlying();
}
@Override
public void onDialogYes(String dialogTag) {
switch (dialogTag) {
case DRONIE_CREATION_DIALOG_TAG:
missionProxy.makeAndUploadDronie(getDrone());
break;
}
}
@Override
public void onDialogNo(String dialogTag) {
}
}