package org.droidplanner.android.fragments.mode; 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.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.o3dr.android.client.Drone; import com.o3dr.android.client.apis.FollowApi; 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.gcs.follow.FollowState; import com.o3dr.services.android.lib.gcs.follow.FollowType; import org.beyene.sius.unit.length.LengthUnit; import org.droidplanner.android.R; import org.droidplanner.android.fragments.DroneMap; import org.droidplanner.android.graphic.map.GuidedScanROIMarkerInfo; import org.droidplanner.android.utils.Utils; import org.droidplanner.android.utils.prefs.DroidPlannerPrefs; import org.droidplanner.android.utils.unit.providers.length.LengthUnitProvider; import org.droidplanner.android.view.spinnerWheel.CardWheelHorizontalView; import org.droidplanner.android.view.spinnerWheel.adapters.LengthWheelAdapter; public class ModeFollowFragment extends ModeGuidedFragment implements OnItemSelectedListener { private static final double DEFAULT_MIN_RADIUS = 2; //meters private static final IntentFilter eventFilter = new IntentFilter(AttributeEvent.FOLLOW_UPDATE); private final BroadcastReceiver eventReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); switch (action) { case AttributeEvent.FOLLOW_UPDATE: final FollowState followState = getDrone().getAttribute(AttributeType.FOLLOW_STATE); if (followState != null) { final FollowType followType = followState.getMode(); onFollowTypeUpdate(followType, followState.getParams()); } break; } } }; private final GuidedScanROIMarkerInfo roiMarkerInfo = new GuidedScanROIMarkerInfo(); private FollowType lastFollowType; private Bundle lastFollowParams; private TextView modeDescription; private Spinner spinner; private ArrayAdapter<FollowType> adapter; private CardWheelHorizontalView<LengthUnit> mRadiusWheel; private CardWheelHorizontalView<LengthUnit> roiHeightWheel; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_mode_follow, container, false); } @Override public void onViewCreated(View parentView, Bundle savedInstanceState) { super.onViewCreated(parentView, savedInstanceState); modeDescription = (TextView) parentView.findViewById(R.id.ModeDetail); final Context context = getContext(); final LengthUnitProvider lengthUP = getLengthUnitProvider(); final DroidPlannerPrefs dpPrefs = getAppPrefs(); final LengthWheelAdapter radiusAdapter = new LengthWheelAdapter(context, R.layout.wheel_text_centered, lengthUP.boxBaseValueToTarget(Utils.MIN_DISTANCE), lengthUP.boxBaseValueToTarget(Utils.MAX_DISTANCE)); mRadiusWheel = (CardWheelHorizontalView<LengthUnit>) parentView.findViewById(R.id.radius_spinner); mRadiusWheel.setViewAdapter(radiusAdapter); mRadiusWheel.addScrollListener(this); final LengthWheelAdapter roiHeightAdapter = new LengthWheelAdapter(context, R.layout.wheel_text_centered, lengthUP.boxBaseValueToTarget(dpPrefs.getMinAltitude()), lengthUP.boxBaseValueToTarget(dpPrefs.getMaxAltitude())); roiHeightWheel = (CardWheelHorizontalView<LengthUnit>) parentView.findViewById(R.id.roi_height_spinner); roiHeightWheel.setViewAdapter(roiHeightAdapter); roiHeightWheel.addScrollListener(this); spinner = (Spinner) parentView.findViewById(R.id.follow_type_spinner); adapter = new FollowTypesAdapter(context, false); spinner.setAdapter(adapter); spinner.setOnItemSelectedListener(this); } @Override public void onDestroyView() { super.onDestroyView(); if (mRadiusWheel != null) { mRadiusWheel.removeChangingListener(this); } } @Override public void onApiConnected() { super.onApiConnected(); final FollowState followState = getDrone().getAttribute(AttributeType.FOLLOW_STATE); if (followState != null) { final FollowType followType = followState.getMode(); onFollowTypeUpdate(followType, followState.getParams()); } parent.addMarker(roiMarkerInfo); getBroadcastManager().registerReceiver(eventReceiver, eventFilter); } private void onFollowTypeUpdate(FollowType followType, Bundle params) { Context context = getContext(); if(followType == null || context == null) { return; } if(followType != lastFollowType) { lastFollowType = followType; spinner.setSelection(adapter.getPosition(followType)); updateModeDescription(followType); } if(params != null){ params.setClassLoader(context.getClassLoader()); } if(!Utils.equalBundles(params, lastFollowParams)) { lastFollowParams = params; if (followType.hasParam(FollowType.EXTRA_FOLLOW_RADIUS)) { double radius = DEFAULT_MIN_RADIUS; if (params != null) { radius = params.getDouble(FollowType.EXTRA_FOLLOW_RADIUS, DEFAULT_MIN_RADIUS); } mRadiusWheel.setVisibility(View.VISIBLE); mRadiusWheel.setCurrentValue((getLengthUnitProvider().boxBaseValueToTarget(radius))); } else { mRadiusWheel.setVisibility(View.GONE); } double roiHeight = GuidedScanROIMarkerInfo.DEFAULT_FOLLOW_ROI_ALTITUDE; LatLong roiTarget = null; if (followType.hasParam(FollowType.EXTRA_FOLLOW_ROI_TARGET)) { roiTarget = roiMarkerInfo.getPosition(); if (params != null) { params.setClassLoader(LatLong.class.getClassLoader()); roiTarget = params.getParcelable(FollowType.EXTRA_FOLLOW_ROI_TARGET); } if (roiTarget instanceof LatLongAlt) roiHeight = ((LatLongAlt) roiTarget).getAltitude(); } roiHeightWheel.setCurrentValue(getLengthUnitProvider().boxBaseValueToTarget(roiHeight)); updateROITargetMarker(roiTarget); } } private void updateModeDescription(FollowType followType) { if(followType == null) return; switch (followType) { case GUIDED_SCAN: modeDescription.setText(R.string.mode_follow_guided_scan); break; default: modeDescription.setText(R.string.mode_follow); break; } } @Override public void onApiDisconnected() { super.onApiDisconnected(); parent.removeMarker(roiMarkerInfo); getBroadcastManager().unregisterReceiver(eventReceiver); } @Override public void onScrollingEnded(CardWheelHorizontalView cardWheel, LengthUnit oldValue, LengthUnit newValue) { final Drone drone = getDrone(); switch (cardWheel.getId()) { case R.id.radius_spinner: if (drone.isConnected()) { Bundle params = new Bundle(); params.putDouble(FollowType.EXTRA_FOLLOW_RADIUS, newValue.toBase().getValue()); FollowApi.getApi(drone).updateFollowParams(params); } break; case R.id.roi_height_spinner: if (drone.isConnected()) { final LatLongAlt roiCoord = roiMarkerInfo.getPosition(); if (roiCoord != null) { roiCoord.setAltitude(newValue.toBase().getValue()); pushROITargetToVehicle(drone, roiCoord); } } break; default: super.onScrollingEnded(cardWheel, oldValue, newValue); break; } } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { final FollowType type = adapter.getItem(position); getAppPrefs().setLastKnownFollowType(type); final Drone drone = getDrone(); if (drone.isConnected()) { FollowApi.getApi(drone).enableFollowMe(type); } } @Override public void onNothingSelected(AdapterView<?> arg0) { } @Override public void onGuidedClick(LatLong coord) { final Drone drone = getDrone(); final FollowState followState = drone.getAttribute(AttributeType.FOLLOW_STATE); if (followState != null && followState.isEnabled() && followState.getMode().hasParam(FollowType.EXTRA_FOLLOW_ROI_TARGET)) { Toast.makeText(getContext(), R.string.guided_scan_roi_set_message, Toast.LENGTH_LONG).show(); final double roiHeight = roiHeightWheel.getCurrentValue().toBase().getValue(); final LatLongAlt roiCoord = new LatLongAlt(coord.getLatitude(), coord.getLongitude(), roiHeight); pushROITargetToVehicle(drone, roiCoord); updateROITargetMarker(coord); } else { super.onGuidedClick(coord); } } private void pushROITargetToVehicle(Drone drone, LatLongAlt roiCoord) { if (roiCoord == null) return; Bundle params = new Bundle(); params.putParcelable(FollowType.EXTRA_FOLLOW_ROI_TARGET, roiCoord); FollowApi.getApi(drone).updateFollowParams(params); } private void updateROITargetMarker(LatLong target) { roiMarkerInfo.setPosition(target); getBroadcastManager().sendBroadcast(new Intent(DroneMap.ACTION_UPDATE_MAP)); if (target == null) { roiHeightWheel.setVisibility(View.GONE); } else { roiHeightWheel.setVisibility(View.VISIBLE); } } private static class FollowTypesAdapter extends ArrayAdapter<FollowType> { private final LayoutInflater inflater; public FollowTypesAdapter(Context context, boolean isAdvancedMenuEnabled) { super(context, 0, FollowType.getFollowTypes(isAdvancedMenuEnabled)); inflater = LayoutInflater.from(context); } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView view; if (convertView == null) { view = (TextView) inflater.inflate(R.layout.list_item_follow_types, parent, false); } else { view = (TextView) convertView; } final FollowType followType = getItem(position); view.setText(followType.getTypeLabel()); return view; } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } } }