package com.maciekjanusz.compassproject.ui; import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManager; import android.location.Location; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; import android.support.v7.widget.SwitchCompat; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.TextView; import com.google.android.gms.maps.model.LatLng; import com.maciekjanusz.compassproject.R; import com.maciekjanusz.compassproject.input.LocationInputDialogFragment; import com.maciekjanusz.compassproject.navigation.NavigationBundle; import com.maciekjanusz.compassproject.navigation.NavigationService; import com.maciekjanusz.compassproject.navigation.ServiceMessage; import com.maciekjanusz.compassproject.navigation.ServiceState; import com.maciekjanusz.compassproject.sensor.Compass; import com.maciekjanusz.compassproject.util.ScreenRotationAware; import com.maciekjanusz.compassproject.util.SimpleDisplayListener; import com.maciekjanusz.compassproject.util.ValueFormatter; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import de.greenrobot.event.EventBus; import static com.maciekjanusz.compassproject.preferences.AppPreferences.isWidgetServiceRunning; import static com.maciekjanusz.compassproject.util.CompassMath.adjustBearing; import static com.maciekjanusz.compassproject.util.CompassMath.calculateTimeToReach; public class CompassFragment extends Fragment implements Compass.CompassListener, ScreenRotationAware { private static final String LOCATION_INPUT_DIALOG_TAG = "location_input_dialog"; @Bind(R.id.compass_view) CompassView compassView; @Bind(R.id.action_widget_switch) SwitchCompat widgetSwitch; @Bind(R.id.pick_place_button) FloatingActionButton pickPlaceButton; @Bind(R.id.pitch_view) TextView pitchView; @Bind(R.id.roll_view) TextView rollView; @Bind(R.id.navigation_status_layout) LinearLayout navigationStatusLayout; @Bind(R.id.destination_text_view) TextView destinationTextView; @Bind(R.id.speed_text_view) TextView speedTextView; @Bind(R.id.distance_text_view) TextView distanceTextView; @Bind(R.id.estimated_time_text_view) TextView estimatedTimeTextView; private Compass compass; private ValueFormatter valueFormatter; private WindowManager windowManager; private DisplayManager displayManager; private final LocationInputDialogFragment locationInputDialogFragment = new LocationInputDialogFragment(); private ServiceState currentServiceState = ServiceState.NAVIGATION_STOPPED; private volatile int deviceScreenRotation; private int currentPitch; private int currentRoll; private DisplayManager.DisplayListener displayListener = new SimpleDisplayListener() { @Override public void onDisplayChanged(int displayId) { updateRotation(); } }; @Override public void onAttach(Context context) { super.onAttach(context); windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); compass = new Compass(getContext(), this); valueFormatter = new ValueFormatter(getContext()); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_compass, container, false); ButterKnife.bind(this, rootView); // views bound -> references are now valid initCompassView(); initWidgetSwitch(); return rootView; } @Override public void onDestroyView() { super.onDestroyView(); // cleanup ButterKnife.unbind(this); } @Override public void onResume() { super.onResume(); // update parameters valueFormatter.loadPrefs(getContext()); updateRotation(); compass.start(); // register to eventBus and request nav service state EventBus eventBus = EventBus.getDefault(); eventBus.register(this); eventBus.post(ServiceMessage.REQUEST_NAVIGATION_SERVICE_STATE); } @Override public void onStart() { super.onStart(); displayManager.registerDisplayListener(displayListener, null); } @Override public void onStop() { displayManager.unregisterDisplayListener(displayListener); super.onStop(); } @Override public void onPause() { EventBus.getDefault().unregister(this); compass.stop(); super.onPause(); } private void initCompassView() { compassView.setCompassEnabled(true); compassView.setCardinalsEnabled(true); compassView.setHasBackground(true); } private void initWidgetSwitch() { widgetSwitch.setChecked(isWidgetServiceRunning(getContext())); widgetSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Intent serviceIntent = new Intent(getContext(), ScreenWidgetService.class); if (isChecked) { getContext().startService(serviceIntent); ((CompassActivity) getActivity()).showWidgetInfoSnackbar(); } else { getContext().stopService(serviceIntent); } } }); } @Override public void onCompassStateChanged(float bearing, float pitch, float roll) { updateCompass(bearing, roll); updatePitchAndRoll(pitch, roll); } @Override public synchronized void updateRotation() { deviceScreenRotation = windowManager.getDefaultDisplay().getRotation(); } @SuppressWarnings("unused") public void onEvent(@NonNull NavigationBundle navigationBundle) { Location location = navigationBundle.getLocation(); float distance = navigationBundle.getDistance(); float speed = location.getSpeed(); // navBundle received -> show navigation status and heading navigationStatusLayout.setVisibility(View.VISIBLE); compassView.setNavigationEnabled(true); // set bearing float bearing = navigationBundle.getBearing(); bearing = adjustBearing(bearing, currentRoll, 0); compassView.setNavigationBearing(bearing); // update navigation status LatLng destination = navigationBundle.getDestination(); distanceTextView.setText(valueFormatter.formatDistance(distance)); destinationTextView.setText(valueFormatter.formatCoordinates( destination.latitude, destination.longitude)); speedTextView.setText(valueFormatter.formatSpeed(speed)); // try to calculate estimated reach time only if speed > 0 if(speed == 0) { estimatedTimeTextView.setText(getString(R.string.infinity)); } else { long estimatedTime = calculateTimeToReach(speed, distance); estimatedTimeTextView.setText(valueFormatter.formatDuration(estimatedTime)); } } @SuppressWarnings("unused") public void onEvent(@NonNull ServiceState serviceState) { currentServiceState = serviceState; switch(serviceState) { case NAVIGATION_RUNNING: changeFloatingButton(true); break; case NAVIGATION_STOPPED: compassView.setNavigationEnabled(false); changeFloatingButton(false); navigationStatusLayout.setVisibility(View.INVISIBLE); break; } } private void changeFloatingButton(boolean navigationServiceRunning) { if (navigationServiceRunning) { pickPlaceButton.setImageResource(R.drawable.ic_cancel_white_48dp); } else { pickPlaceButton.setImageResource(R.drawable.ic_navigation_white_48dp); } } @SuppressWarnings("unused") @OnClick(R.id.pick_place_button) public void onFloatingButtonClick() { switch (currentServiceState) { case NAVIGATION_RUNNING: getContext().stopService(new Intent(getContext(), NavigationService.class)); break; case NAVIGATION_STOPPED: if(!locationInputDialogFragment.isAdded()) { locationInputDialogFragment.show(getActivity().getSupportFragmentManager(), LOCATION_INPUT_DIALOG_TAG); } break; } } private void updatePitchAndRoll(float pitchF, float rollF) { // cast to int so they can be compared with current int pitch = (int) pitchF; int roll = (int) rollF; /* Update views ONLY if values has changed, using valueFormatter, - or watch the sawtooth on memory monitor... */ if(pitch != currentPitch) { currentPitch = pitch; pitchView.setText(valueFormatter.formatDegreeValue(pitch)); } if(roll != currentRoll) { currentRoll = roll; rollView.setText(valueFormatter.formatDegreeValue(roll)); } } private void updateCompass(float bearing, float roll) { // check if view is not null (samsung bug) if(compassView != null) { // ensure that the value is correct with current roll & deviceScreenRotation bearing = adjustBearing(bearing, roll, deviceScreenRotation); // update compass view compassView.setCompassBearing(-bearing); } } }