package org.droidplanner.android.fragments; 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.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.google.android.gms.maps.model.LatLng; import com.o3dr.android.client.apis.ControlApi; import com.o3dr.services.android.lib.coordinate.LatLong; import com.o3dr.services.android.lib.coordinate.LatLongAlt; import com.o3dr.services.android.lib.drone.attribute.AttributeEvent; import com.o3dr.services.android.lib.drone.attribute.AttributeType; import com.o3dr.services.android.lib.drone.mission.item.MissionItem; import com.o3dr.services.android.lib.drone.property.Gps; import com.o3dr.services.android.lib.drone.property.GuidedState; import com.o3dr.services.android.lib.drone.property.State; import org.droidplanner.android.R; import org.droidplanner.android.activities.EditorActivity; import org.droidplanner.android.dialogs.GuidedDialog; import org.droidplanner.android.dialogs.GuidedDialog.GuidedDialogListener; import org.droidplanner.android.graphic.map.GraphicHome; import org.droidplanner.android.maps.DPMap; import org.droidplanner.android.maps.MarkerInfo; import org.droidplanner.android.proxy.mission.MissionProxy; import org.droidplanner.android.utils.MapUtils; import org.droidplanner.android.utils.SpaceTime; import org.droidplanner.android.utils.prefs.AutoPanMode; import java.util.List; public class FlightMapFragment extends DroneMap implements DPMap.OnMapLongClickListener, DPMap.OnMarkerClickListener, DPMap.OnMarkerDragListener, GuidedDialogListener { public interface OnGuidedClickListener { void onGuidedClick(LatLong coord); } private static final int MAX_TOASTS_FOR_LOCATION_PRESS = 3; private static final String PREF_USER_LOCATION_FIRST_PRESS = "pref_user_location_first_press"; private static final int DEFAULT_USER_LOCATION_FIRST_PRESS = 0; private static final String PREF_DRONE_LOCATION_FIRST_PRESS = "pref_drone_location_first_press"; private static final int DEFAULT_DRONE_LOCATION_FIRST_PRESS = 0; /** * The map should zoom on the user location the first time it's acquired. This flag helps * enable the behavior. */ private static boolean didZoomOnUserLocation = false; private static final IntentFilter eventFilter = new IntentFilter(AttributeEvent.STATE_ARMING); private final BroadcastReceiver eventReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (AttributeEvent.STATE_ARMING.equals(action)) { final State droneState = drone.getAttribute(AttributeType.STATE); if (droneState.isArmed()) { clearFlightPath(); } } } }; private OnGuidedClickListener guidedClickListener; public void setGuidedClickListener(OnGuidedClickListener guidedClickListener) { this.guidedClickListener = guidedClickListener; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { setHasOptionsMenu(true); View view = super.onCreateView(inflater, viewGroup, bundle); mMapFragment.setOnMapLongClickListener(this); mMapFragment.setOnMarkerDragListener(this); mMapFragment.setOnMarkerClickListener(this); return view; } @Override public void onResume() { super.onResume(); mMapFragment.selectAutoPanMode(mAppPrefs.getAutoPanMode()); if (!didZoomOnUserLocation) { super.goToMyLocation(); didZoomOnUserLocation = true; } } @Override protected LatLongAlt getCurrentFlightPoint(){ LatLongAlt space = super.getCurrentFlightPoint(); if (space != null) { return new SpaceTime(space, System.currentTimeMillis()); } return null; } @Override public void onPause() { super.onPause(); mMapFragment.selectAutoPanMode(AutoPanMode.DISABLED); } @Override protected boolean showFlightPath(){ return true; } @Override public boolean setAutoPanMode(AutoPanMode target) { // Update the map panning preferences. if (mAppPrefs != null) mAppPrefs.setAutoPanMode(target); if (mMapFragment != null) mMapFragment.selectAutoPanMode(target); return true; } @Override public void onApiConnected() { super.onApiConnected(); getBroadcastManager().registerReceiver(eventReceiver, eventFilter); } @Override public void onApiDisconnected() { super.onApiDisconnected(); getBroadcastManager().unregisterReceiver(eventReceiver); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_flight_map, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) { case R.id.menu_map_clear_flight_path: clearFlightPath(); return true; case R.id.menu_export_flight_path_as_mission: if (flightPathPoints.isEmpty()) { Toast.makeText(getContext(), R.string.error_empty_flight_path, Toast.LENGTH_LONG).show(); return true; } List<MissionItem> exportedMissionItems = MapUtils.exportPathAsMissionItems(flightPathPoints, 0.00012); MissionProxy missionProxy = getMissionProxy(); missionProxy.clear(); missionProxy.addMissionItems(exportedMissionItems); startActivity(new Intent(getActivity(), EditorActivity.class)); clearFlightPath(); Toast.makeText(getContext(), R.string.warning_check_exported_mission, Toast.LENGTH_LONG).show(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onMapLongClick(LatLong coord) { if (drone != null && drone.isConnected()) { final GuidedState guidedState = drone.getAttribute(AttributeType.GUIDED_STATE); if (guidedState.isInitialized()) { if(guidedClickListener != null) guidedClickListener.onGuidedClick(coord); } else { GuidedDialog dialog = new GuidedDialog(); dialog.setCoord(MapUtils.coordToLatLng(coord)); dialog.setListener(this); dialog.show(getChildFragmentManager(), "GUIDED dialog"); } } } @Override public void onForcedGuidedPoint(LatLng coord) { try { ControlApi.getApi(drone).goTo(MapUtils.latLngToCoord(coord), true, null); } catch (Exception e) { Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); } } @Override public void onMarkerDragStart(MarkerInfo markerInfo) { } @Override public void onMarkerDrag(MarkerInfo markerInfo) { } @Override public void onMarkerDragEnd(MarkerInfo markerInfo) { if (!(markerInfo instanceof GraphicHome)) { ControlApi.getApi(drone).goTo(markerInfo.getPosition(), false, null); } } @Override public boolean onMarkerClick(MarkerInfo markerInfo) { if(markerInfo == null) return false; ControlApi.getApi(drone).goTo(markerInfo.getPosition(), false, null); return true; } @Override protected boolean isMissionDraggable() { return false; } @Override public void goToMyLocation() { super.goToMyLocation(); int pressCount = mAppPrefs.prefs.getInt(PREF_USER_LOCATION_FIRST_PRESS, DEFAULT_USER_LOCATION_FIRST_PRESS); if (pressCount < MAX_TOASTS_FOR_LOCATION_PRESS) { Toast.makeText(context, R.string.user_autopan_long_press, Toast.LENGTH_LONG).show(); mAppPrefs.prefs.edit().putInt(PREF_USER_LOCATION_FIRST_PRESS, pressCount + 1).apply(); } } @Override public void goToDroneLocation() { super.goToDroneLocation(); if(this.drone == null) return; final Gps droneGps = this.drone.getAttribute(AttributeType.GPS); if (droneGps == null || !droneGps.isValid()) return; final int pressCount = mAppPrefs.prefs.getInt(PREF_DRONE_LOCATION_FIRST_PRESS, DEFAULT_DRONE_LOCATION_FIRST_PRESS); if (pressCount < MAX_TOASTS_FOR_LOCATION_PRESS) { Toast.makeText(context, R.string.drone_autopan_long_press, Toast.LENGTH_LONG).show(); mAppPrefs.prefs.edit().putInt(PREF_DRONE_LOCATION_FIRST_PRESS, pressCount + 1).apply(); } } }