cs306-versus/versus-app

View on GitHub
app/src/main/java/com/github/versus/LocationFragment.java

Summary

Maintainability
C
1 day
Test Coverage
B
82%
package com.github.versus;

import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import static java.lang.Math.toRadians;

import android.animation.ValueAnimator;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.InputType;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
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.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.github.versus.db.DataBaseManager;
import com.github.versus.db.DummyLocationManager;
import com.github.versus.user.CustomPlace;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.libraries.places.widget.AutocompleteSupportFragment;
import com.google.android.libraries.places.widget.listener.PlaceSelectionListener;


import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;




public class LocationFragment extends Fragment implements OnMapReadyCallback {

    private static final String TAG = LocationFragment.class.getSimpleName();
    private static final int DEFAULT_ZOOM = 15;
    private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;

    private static final String KEY_CAMERA_POSITION = "camera_position";
    private static final String KEY_LOCATION = "location";

    public static GoogleMap map;//TODO

    private static LatLng localPos;
    private static boolean locationPermissionGranted;
    private static Location lastKnownLocation;

    private static LatLng google;
    private static String[] likelyPlaceNames;
    private static String[] likelyPlaceAddresses;
    private static LatLng[] likelyPlaceLatLngs;

    // A default location (Sydney, Australia) and default zoom to use when location permission is
    // not granted.
    private final LatLng defaultLocation = new LatLng(-33.8523341, 151.2106085);
    private CameraPosition cameraPosition;
    // The entry point to the Places API.
    private PlacesClient placesClient;
    // The entry point to the Fused Location Provider.
    private FusedLocationProviderClient fusedLocationProviderClient;
    private DataBaseManager dummyLocationManager;

    private List<CustomPlace> customPlaces;
    public static  String API_KEY;

    private LatLng   bc = new LatLng(46.51906462963576, 6.561923350291548);
    private LatLng selectedPlace ;

    // Threshold distance to consider a field as nearby (in meters)
    private double thresholdDistanceInput=1000;
    private  AutocompleteSupportFragment autocompleteFragment;
    private Marker blueMarker ;
    private Marker redMarker;
    private Polyline lastDrawnLine;
    private double distanceValue;
    private Marker blinkingMarker;


    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_location, container, false);
        //call setHasOptionsMenu(true) to notify the fragment
        // that it has options menu items that need to be created


        setHasOptionsMenu(true);

        // Retrieve location and camera position from saved instance state.
        if (savedInstanceState != null) {
            lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION);
            cameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION);
        }

        // Construct a PlacesClient and retrieve the API Key from local.properties file
        try {
            API_KEY = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA).metaData.getString("com.google.android.geo.API_KEY");
            Places.initialize(getActivity().getApplicationContext(), API_KEY);


        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(e);
        }
        placesClient = Places.createClient(getActivity());

        // Construct a FusedLocationProviderClient.
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());

        // Build the map
        SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
        Button nearMeButton = view.findViewById(R.id.near_me_button);
        nearMeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Replace originLatLng and destinationLatLng with the actual LatLng objects

                    if(redMarker!= null){
                        redMarker.remove();
                    }
                    if(blinkingMarker!= null){
                        blinkingMarker.remove();
                    }
                    if (lastDrawnLine != null) {

                        lastDrawnLine.remove();
                    }
                    if(blueMarker != null){
                        findClosestFields(blueMarker.getPosition());
                    }
                    else {
                        findClosestFields(localPos);
                    }
                }



        });


        // Get the AutocompleteSupportFragment from the FragmentManager using its ID
        autocompleteFragment = (AutocompleteSupportFragment)
                getChildFragmentManager().findFragmentById(R.id.autocomplete_location_search);

        // Set the visibility of the autocompleteFragment's view to GONE, meaning it will not be visible, nor take up any space
        autocompleteFragment.getView().setVisibility(View.GONE);

        // Set the fields that should be included for the place (location) selected in the search bar
        autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG));

        // Set a listener that gets triggered when a place is selected from the search suggestions
        autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                // Log the details of the selected place
                Log.i(TAG, "Place: " + place.getName() + ", " + place.getId());

                // Store the LatLng of the selected place
                selectedPlace = place.getLatLng();

                // If the map is not null and a place has been selected, move the camera to the selected place and hide the autocompleteFragment's view
                if (map != null && selectedPlace != null) {
                    map.moveCamera(CameraUpdateFactory.newLatLngZoom(selectedPlace, DEFAULT_ZOOM));
                    autocompleteFragment.getView().setVisibility(View.GONE);

                    // Remove the previous markers if they exist

                    // Add a new marker at the selected place
                     blueMarker= map.addMarker(new MarkerOptions().title(place.getName()).position(selectedPlace).snippet(place.getAddress()).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)));

                }
            }

            @Override
            public void onError(Status status) {
                // Log if there is any error while selecting a place
                Log.i(TAG, "An error occurred: " + status);
            }
        });



        return view;
    }

    /**
     * Manipulates the map when it's available.
     * This callback is triggered when the map is ready to be used.
     */
    @Override
    public void onMapReady(GoogleMap map) {
        LocationFragment.map = map;
        map.getUiSettings().setZoomControlsEnabled(true);
        map.getUiSettings().setCompassEnabled(true);
        LocationFragment.map.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
            // Return null here, so that getInfoContents() is called next.
            public View getInfoWindow(Marker arg0) {
                return null;
            }

            @Override
            public View getInfoContents(Marker marker) {
                // Inflate the layouts for the info window, title and snippet.
                View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_contents, getActivity().findViewById(R.id.map), false);

                TextView title = infoWindow.findViewById(R.id.title);
                title.setText(marker.getTitle());

                TextView snippet = infoWindow.findViewById(R.id.snippet);
                snippet.setText(marker.getSnippet());

                return infoWindow;
            }
        });


        // Add markers for EPFL and Satellite

        google = new LatLng(37.42, -122.084);
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(google, 15));

        dummyLocationManager = new DummyLocationManager();
        try {
            customPlaces = (List<CustomPlace>) dummyLocationManager.fetch("Places").get();
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {

            throw new RuntimeException(e);
        }
        //  map.moveCamera(CameraUpdateFactory.newLatLng(epfl));
        map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
            @Override
            public void onInfoWindowClick(Marker marker) {
                LatLng position = marker.getPosition();
                String message = "Marker clicked at: " + position.latitude + ", " + position.longitude;
                Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
            }
        });


        // Prompt the user for permission.
        getLocationPermission();

        // Turn on the My Location layer and the related control on the map.
        updateLocationUI();

        // Get the current location of the device and set the position of the map.
        getDeviceLocation();
    }

    /**
     * Saves the state of the map when the activity is paused.
     *
     * @param outState The Bundle object to save the state.
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (map != null) {
            outState.putParcelable(KEY_CAMERA_POSITION, map.getCameraPosition());
            outState.putParcelable(KEY_LOCATION, lastKnownLocation);
        }
        super.onSaveInstanceState(outState);
    }

    /**
     * Sets up the options menu.
     *
     * @param menu The options menu.
     * @return Boolean.
     */

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {

        inflater.inflate(R.menu.current_place_menu, menu);

        // Get the menu item that contains the EditText view

        super.onCreateOptionsMenu(menu, inflater);
    }

    /**
     * Handles a click on the menu option to get a place or choose a random location.
     *
     * @param item The menu item to handle.
     * @return Boolean.
     */

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        if (item.getItemId() == R.id.option_choose_location) {

            if(redMarker!= null){
                redMarker.remove();
            }
            if(blueMarker != null){
                blueMarker.remove();
            }
            if(blinkingMarker!= null){
                blinkingMarker.remove();
            }
            if (lastDrawnLine != null) {
                lastDrawnLine.remove();
            }


          enableCustomLocationSelection();
        }
        else if (item.getItemId() == R.id.option_choose_radius) {

            if(redMarker!= null){
                redMarker.remove();
            }

            if(blinkingMarker!= null){
                blinkingMarker.remove();
            }
            if (lastDrawnLine != null) {
                lastDrawnLine.remove();
            }
          map.setOnMapClickListener(null);

          chooseDefaultRadius();
        }
        else if (item.getItemId() == R.id.search_bar) {

            if(blueMarker != null){
                blueMarker.remove();
            }
            if(redMarker!= null){
                redMarker.remove();
            }
            if(blinkingMarker!= null){
                blinkingMarker.remove();
            }
            if (lastDrawnLine != null) {
                lastDrawnLine.remove();
            }
          map.setOnMapClickListener(null);

          autocompleteFragment.getView().setVisibility(View.VISIBLE);
        }

        return true;
    }
    /**
     * Enables custom location selection by setting a map click listener.
     * When the map is clicked, it calls the findClosestFields method to find the closest fields to the clicked position.
     */
    private void enableCustomLocationSelection() {

        map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {

                if(redMarker!= null){
                    redMarker.remove();
                }

                if(blinkingMarker!= null){
                    blinkingMarker.remove();
                }
                if (lastDrawnLine != null) {

                    lastDrawnLine.remove();
                }
                if (blueMarker!= null) {

                    blueMarker.remove();
                }

                // Find closest fields to the clicked position
                findClosestFields(latLng);
            }
        });
    }



    /**
     * Finds the closest fields to a clicked position on the map.
     * Iterates through the custom places and finds the ones within the threshold distance.
     * Prepares the data for the showPlacesList() method and shows the list of nearby fields.
     *
     * @param clickedPosition LatLng object representing the position where the user clicked on the map.
     */
    private void findClosestFields(LatLng clickedPosition) {

        // Add a new marker to the map at the clicked position
        blueMarker=map.addMarker(new MarkerOptions().position(clickedPosition).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)).title("Clicked Location"));

        // Iterate through the custom places and find the ones within the threshold distance
        List<CustomPlace> nearbyFields = customPlaces.stream().filter(place -> haversineDistance(clickedPosition, place.latLng) <= thresholdDistanceInput).collect(Collectors.toList());


        // Prepare the data for the showPlacesList() method
        if(!nearbyFields.isEmpty()) {
            likelyPlaceNames = new String[nearbyFields.size()];
            likelyPlaceAddresses = new String[nearbyFields.size()];
            likelyPlaceLatLngs = new LatLng[nearbyFields.size()];

            for (int i = 0; i < nearbyFields.size(); i++) {
                likelyPlaceNames[i] = nearbyFields.get(i).name;
                likelyPlaceAddresses[i] = nearbyFields.get(i).address;
                likelyPlaceLatLngs[i] = nearbyFields.get(i).latLng;
            }
            // Show the list of nearby fields
            showPlacesList();
        }
        else{
            showToast("No locations found within the selected radius");
        }
    }

    /**
     * Gets the current location of the device, and positions the map's camera.
     */
    private void getDeviceLocation() {
        /*
         * Get the best and most recent location of the device, which may be null in rare
         * cases when a location is not available.
         */

        try {
            if (locationPermissionGranted) {
                Task<Location> locationResult = fusedLocationProviderClient.getLastLocation();
                locationResult.addOnCompleteListener(getActivity(), new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        if (task.isSuccessful()) {
                            // Set the map's camera position to the current location of the device.
                            lastKnownLocation = task.getResult();

                            if (lastKnownLocation != null) {
                                localPos = new LatLng(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude());

                                map.moveCamera(CameraUpdateFactory.newLatLngZoom(localPos, DEFAULT_ZOOM));
                            }
                        } else {
                            Log.d(TAG, "Current location is null. Using defaults.");
                            Log.e(TAG, "Exception: %s", task.getException());
                            map.moveCamera(CameraUpdateFactory.newLatLngZoom(defaultLocation, DEFAULT_ZOOM));
                            map.getUiSettings().setMyLocationButtonEnabled(false);
                        }
                    }
                });
            }
        } catch (SecurityException e) {
            Log.e("Exception: %s", e.getMessage(), e);
        }
    }


    /**
     * Prompts the user for permission to use the device location.
     */

    void getLocationPermission() {
        /*
         * Request location permission, so that we can get the location of the
         * device. The result of the permission request is handled by a callback,
         * onRequestPermissionsResult.
         */
        if (ContextCompat.checkSelfPermission(getActivity().getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            locationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(getActivity(), new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }


    /**
     * Handles the result of the request for location permissions.
     *
     * @param requestCode  The request code passed to requestPermissions().
     * @param permissions  The requested permissions.
     * @param grantResults The grant results for the corresponding permissions.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        locationPermissionGranted = false;
        if (requestCode == PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION) {// If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                locationPermissionGranted = true;
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
        updateLocationUI();
    }





    /**
     * Updates the map's UI settings based on whether the user has granted location permission.
     */
    private void updateLocationUI() {
        if (map == null) {
            return;
        }
        try {
            if (locationPermissionGranted) {
                map.setMyLocationEnabled(true);
                map.getUiSettings().setMyLocationButtonEnabled(true);
            } else {
                map.setMyLocationEnabled(false);
                map.getUiSettings().setMyLocationButtonEnabled(false);
                lastKnownLocation = null;
                getLocationPermission();
            }
        } catch (SecurityException e) {
            Log.e("Exception: %s", e.getMessage());
        }
    }
    private void chooseDefaultRadius(){

        View view;
        EditText radiusInput;
        LayoutInflater inflater = requireActivity().getLayoutInflater();

        // Inflate custom layout
        view = inflater.inflate(R.layout.custom_radius_layout, null);

        // Find your EditText view
        radiusInput = view.findViewById(R.id.edit_text_radius3);

        // Get a reference to the EditText view in the layout

        radiusInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
        radiusInput.setHint("Enter radius (in meters)");

        // Create a dialog to display the EditText view
        AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.CustomAlertDialog).setView(view).setPositiveButton("Enter", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Get the radius entered by the user
                String radiusStr = radiusInput.getText().toString();
                if (!TextUtils.isEmpty(radiusStr)) {
                    thresholdDistanceInput = Float.parseFloat(radiusInput.getText().toString());
                    dialog.dismiss();

                } else {
                    dialog.dismiss();
                }

            }
        }).setNegativeButton("Cancel", null).create();

        // Customize the dialog window appearance here
        Window window = dialog.getWindow();
        if (window != null) {
           changeWindowDimensions(window);
        }


        dialog.show();
    }

    /**
     * Modifies the dimensions of a given window to certain percentages of the screen's width and height.
     * Also sets the window's background drawable resource.
     *
     * @param window The window that will have its dimensions modified and background set.
     */
    private void changeWindowDimensions(Window window) {
        // Set the window's background drawable resource
        window.setBackgroundDrawableResource(R.drawable.custom_dialog_background);

        // Apply the resizing logic inside a post() call on the window's decor view,
        // which ensures that it is executed after the window has been laid out.
        window.getDecorView().post(() -> {
            // Obtain the current display metrics
            DisplayMetrics displayMetrics = new DisplayMetrics();
            getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

            // Calculate new dimensions as percentages of the screen's width and height
            int dialogWindowWidth = (int) (displayMetrics.widthPixels * 0.85); // 85% of screen width
            int dialogWindowHeight = (int) (displayMetrics.heightPixels * 0.25); // 25% of screen height

            // Create a new WindowManager.LayoutParams object and copy the current window's attributes into it
            WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
            layoutParams.copyFrom(window.getAttributes());

            // Set the new dimensions
            layoutParams.width = dialogWindowWidth;
            layoutParams.height = dialogWindowHeight;

            // Apply the new attributes to the window
            window.setAttributes(layoutParams);
        });
    }

    /**
     * Creates and displays a custom dialog containing a list of nearby custom places.
     * When a user selects a place from the list, a blinking marker is added to the selected place on the map,
     * and the camera moves to focus on the selected place with the default zoom level.
     */

    private void showPlacesList() {
        // Create a custom title
        TextView title = new TextView(getActivity());
        title.setText("Select a place");
        title.setBackgroundColor(getResources().getColor(R.color.main_app_color)); // Set the background color to your preference
        title.setPadding(10, 10, 10, 10);
        title.setGravity(Gravity.CENTER);
        title.setTextColor(Color.WHITE);
        title.setTextSize(24);
        title.setTypeface(null, Typeface.BOLD);

        // Create a custom array adapter
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_single_choice, likelyPlaceNames){
            @NonNull
            @Override
            public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
                TextView item = (TextView) super.getView(position, convertView, parent);

                // Set the color of the items in the list
                item.setTextColor(Color.WHITE);

                return item;
            }
        };

        // Create a dialog with radio buttons
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomAlertDialog);
        builder.setCustomTitle(title)
                .setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // User checked an item, update your selectedPlace
                        selectedPlace = likelyPlaceLatLngs[which];
                        redMarker=map.addMarker(new MarkerOptions().title(likelyPlaceNames[which]).position(selectedPlace).snippet(likelyPlaceAddresses[which]));
                        addBlinkingMarker(selectedPlace, likelyPlaceNames[which], likelyPlaceAddresses[which]);
                        map.moveCamera(CameraUpdateFactory.newLatLngZoom(selectedPlace, DEFAULT_ZOOM));
                    }
                })
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        // User clicked OK, you might want to do something here
                        dialog.dismiss();
                    }
                })
                .setNegativeButton("Cancel", null);

        AlertDialog dialog = builder.create();

        // Customize dialog appearance here...
        Window window = dialog.getWindow();
        if (window != null) {
            window.setBackgroundDrawableResource(R.drawable.custom_dialog_background);

            window.getDecorView().post(() -> {
                DisplayMetrics displayMetrics = new DisplayMetrics();
                getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
                int dialogWindowWidth = (int) (displayMetrics.widthPixels * 0.85); // 85% of screen width
                int dialogWindowHeight = (int) (displayMetrics.heightPixels * 0.35); // 25% of screen height

                WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
                layoutParams.copyFrom(window.getAttributes());
                layoutParams.width = dialogWindowWidth;
                layoutParams.height = dialogWindowHeight;
                window.setAttributes(layoutParams);
            });
        }

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface arg0) {
                dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.WHITE);
                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.WHITE);
            }
        });

        dialog.show();
    }




    /**
     * Displays a custom toast with the specified message.
     * The custom toast has a specific layout, background color, and corner radius.
     *
     * @param message The message to be displayed in the custom toast.
     */

    private void showToast(String message) {
        LayoutInflater inflater = getLayoutInflater();
        View layout = inflater.inflate(R.layout.custom_toast, requireActivity().findViewById(R.id.custom_toast_root));

        TextView text = layout.findViewById(R.id.custom_toast_text);
        text.setText(message);

        // Set background programmatically
        GradientDrawable shape = new GradientDrawable();
        shape.setColor(Color.parseColor("#1D4EB5"));
        shape.setCornerRadius(24);
        layout.setBackground(shape);

        Toast toast = new Toast(requireActivity());
        toast.setDuration(Toast.LENGTH_LONG);
        toast.setView(layout);
        toast.show();
    }



    /**
     * Calculates the haversine distance between two LatLng points in meters.
     *
     * @param latLng1 The first LatLng point.
     * @param latLng2 The second LatLng point.
     * @return The haversine distance between the two points in meters.
     */
    private double haversineDistance(LatLng latLng1, LatLng latLng2) {
        double earthRadius = 6371; // Radius of the earth in km
        double dLat = toRadians(latLng2.latitude - latLng1.latitude);
        double dLng = toRadians(latLng2.longitude - latLng1.longitude);
        double a = sin(dLat / 2) * sin(dLat / 2) + cos(toRadians(latLng1.latitude)) * cos(toRadians(latLng2.latitude)) * sin(dLng / 2) * sin(dLng / 2);
        double c = 2 * atan2(sqrt(a), sqrt(1 - a));
        return earthRadius * c * 1000; // Distance in meters
    }
    /**
     * Applies a blinking animation to a given marker on the map.
     *
     * @param marker The marker to apply the blinking animation to.
     */

    private void applyBlinkingAnimation(Marker marker) {
        final Handler handler = new Handler();
        final Runnable blinkingRunnable = new Runnable() {
            @Override
            public void run() {
                marker.setVisible(!marker.isVisible());
                handler.postDelayed(this, 500); // Change the duration of the blinking effect here (in milliseconds)
            }
        };
        handler.postDelayed(blinkingRunnable, 500);
    }



    /**
     * Adds a blinking marker to the map at the specified position with the provided title and snippet.
     *
     * @param position The LatLng position of the marker to be added.
     * @param title    The title of the marker.
     * @param snippet  The snippet of the marker.
     */
    private void addBlinkingMarker(LatLng position, String title, String snippet) {

        FetchDirectionsTask fetchDirectionsTask = new FetchDirectionsTask(blueMarker.getPosition(), selectedPlace);
        fetchDirectionsTask.execute();

        MarkerOptions blinkingMarkerOptions = new MarkerOptions().position(position).title(title).snippet(snippet).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)).alpha(0.5f).visible(false);

        blinkingMarker= map.addMarker(blinkingMarkerOptions);

        applyBlinkingAnimation(blinkingMarker);
    }
    private void drawPath(GoogleMap map, List<LatLng> points) {
        if (lastDrawnLine != null) {
            lastDrawnLine.remove();
        }


        PolylineOptions polylineOptions = new PolylineOptions();
        polylineOptions.addAll(points);
        polylineOptions.width(10);
        polylineOptions.color(Color.BLUE);
        lastDrawnLine = map.addPolyline(polylineOptions);




    }
    private class FetchDirectionsTask extends AsyncTask<Void, Void, List<LatLng>> {
        private LatLng origin;
        private LatLng destination;
        private String errorMessage;

        private String distanceText;

        public FetchDirectionsTask(LatLng origin, LatLng destination) {
            this.origin = origin;
            this.destination = destination;
            this.errorMessage = null;
        }

        @Override
        protected List<LatLng> doInBackground(Void... voids) {
            // Fetch the directions from the Google Maps Directions API
            try {
                // Prepare the URL for the API request
                String url = "https://maps.googleapis.com/maps/api/directions/json?origin=" +
                        origin.latitude + "," + origin.longitude +
                        "&destination=" + destination.latitude + "," + destination.longitude +
                        "&key=" + API_KEY;

                URL apiUrl = new URL(url);
                HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
                connection.setRequestMethod("GET");

                // Get the response from the API
                try (InputStream inputStream = connection.getInputStream();
                     BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
                    StringBuilder stringBuilder = new StringBuilder();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        stringBuilder.append(line);
                    }

                    // Parse the response and extract the path points
                    JSONObject jsonResponse = new JSONObject(stringBuilder.toString());
                    JSONArray routes = jsonResponse.getJSONArray("routes");
                    if (routes.length() > 0) {
                        JSONObject route = routes.getJSONObject(0);
                        JSONArray legs = route.getJSONArray("legs");
                        JSONObject leg = legs.getJSONObject(0);
                        JSONArray steps = leg.getJSONArray("steps");

                        // Get the LatLng points for each step
                        List<LatLng> pathPoints = new ArrayList<>();
                        for (int i = 0; i < steps.length(); i++) {
                            JSONObject step = steps.getJSONObject(i);
                            JSONObject startLocation = step.getJSONObject("start_location");
                            double startLat = startLocation.getDouble("lat");
                            double startLng = startLocation.getDouble("lng");
                            pathPoints.add(new LatLng(startLat, startLng));
                        }

                        // Add the destination point
                        JSONObject endLocation = leg.getJSONObject("end_location");
                        double endLat = endLocation.getDouble("lat");
                        double endLng = endLocation.getDouble("lng");
                        pathPoints.add(new LatLng(endLat, endLng));
                        JSONObject distanceObject = leg.getJSONObject("distance");
                        distanceText = distanceObject.getString("text");
                        distanceValue = distanceObject.getDouble("value");

                        return pathPoints;
                    } else {
                        errorMessage = "No routes found in the API response";
                    }
                }
            }
            catch (Exception e) {
                errorMessage = "An error occurred";
                Log.e("FetchDirectionsTask", errorMessage, e);
            }

            return null;
        }


        @Override
        protected void onPostExecute(List<LatLng> result) {
            if (result != null) {
                drawPath(map, result);
                showToast("Distance: " + distanceText);
            } else {
                if (errorMessage != null) {
                    showToast(errorMessage);
                } else {
                    showToast("Failed to fetch directions");
                }

            }
        }
    }




}