Learn how to find find the current location of an Android device
and display details of the place (a business or other point of interest) at
that location. Follow this tutorial to build an Android app using the
[Maps SDK for Android](https://developers.google.com/maps/documentation/android-sdk),
the [Places SDK for Android](https://developers.google.com/maps/documentation/places/android-sdk),
and the
[fused
location provider](https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi) in the Google Play services location APIs.

> [!NOTE]
> **Note:** This tutorial requires Google Play services version 11.2.0 or later.

## Get the code

[Clone or download the
Google Maps Android API v2 Samples repository](https://github.com/googlemaps-samples/android-samples) from GitHub.

View the Java version of the activity:
<button type="button" class="button-red button expand-control">Show / Hide Code</button>

```
    // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.example.currentplacedetailsonmap;

import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
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.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.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.CircularBounds;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.libraries.places.api.net.SearchNearbyRequest;
import com.google.android.libraries.places.api.net.SearchNearbyResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/** An activity that displays a map showing the place at the device's current location. */
public class MapsActivityCurrentPlace extends AppCompatActivity implements OnMapReadyCallback {

    private static final String TAG = MapsActivityCurrentPlace.class.getSimpleName();
    private GoogleMap map;
    private CameraPosition cameraPosition;

    // The entry point to the Places API.
    private PlacesClient placesClient;

    // The entry point to the Fused Location Provider.
    private FusedLocationProviderClient fusedLocationProviderClient;

    // 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 static final int DEFAULT_ZOOM = 15;
    private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
    private boolean locationPermissionGranted;

    // The geographical location where the device is currently located. That is, the last-known
    // location retrieved by the Fused Location Provider or set via long press.
    Location lastKnownLocation;

    // Keys for storing activity state.
    private static final String KEY_CAMERA_POSITION = "camera_position";
    private static final String KEY_LOCATION = "location";

    // Used for selecting the current place.
    private static final int M_MAX_ENTRIES = 5;
    private static final double SEARCH_RADIUS_METERS = 100.0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

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

        // Retrieve the content view that renders the map.
        setContentView(R.layout.activity_maps);

        ViewCompat.setOnApplyWindowInsetsListener(
                getWindow().getDecorView(),
                (v, insets) -> {
                    Insets systemBars =
                            insets.getInsets(
                                    WindowInsetsCompat.Type.systemBars()
                                            | WindowInsetsCompat.Type.displayCutout());
                    View actionBarContainer =
                            getWindow()
                                    .getDecorView()
                                    .findViewById(androidx.appcompat.R.id.action_bar_container);
                    if (actionBarContainer != null) {
                        actionBarContainer.setPadding(
                                actionBarContainer.getPaddingLeft(),
                                systemBars.top,
                                actionBarContainer.getPaddingRight(),
                                actionBarContainer.getPaddingBottom());
                    }
                    TypedValue typedValue = new TypedValue();
                    int actionBarHeight =
                            getTheme()
                                            .resolveAttribute(
                                                    android.R.attr.actionBarSize, typedValue, true)
                                    ? TypedValue.complexToDimensionPixelSize(
                                            typedValue.data, getResources().getDisplayMetrics())
                                    : 0;
                    if (map != null) {
                        map.setPadding(0, systemBars.top + actionBarHeight, 0, systemBars.bottom);
                    }
                    return insets;
                });
        // Construct a PlacesClient
        Places.initializeWithNewPlacesApiEnabled(getApplicationContext(), BuildConfig.MAPS_API_KEY);
        placesClient = Places.createClient(this);

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

        // Build the map.
        SupportMapFragment mapFragment =
                (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }


    /** Saves the state of the map when the activity is paused. */
    @Override
    protected 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 boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.current_place_menu, menu);
        return true;
    }

    /**
     * Handles a click on the menu option to get a place.
     *
     * @param item The menu item to handle.
     * @return Boolean.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.option_get_place) {
            showCurrentPlace();
        }
        return true;
    }


    /**
     * 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) {
        this.map = map;

        // Use a custom info window adapter to handle multiple lines of text in the
        // info window contents.
        this.map.setInfoWindowAdapter(
                new GoogleMap.InfoWindowAdapter() {

                    @Override
                    // 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,
                                                (FrameLayout) 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;
                    }
                });

        // Allow moving the current location with a long press on the map to easily
        // demo and discover nearby places anywhere in the world.
        this.map.setOnMapLongClickListener(this::setCustomLocation);

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

        WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(getWindow().getDecorView());
        if (rootInsets != null) {
            Insets systemBars =
                    rootInsets.getInsets(
                            WindowInsetsCompat.Type.systemBars()
                                    | WindowInsetsCompat.Type.displayCutout());
            TypedValue typedValue = new TypedValue();
            int actionBarHeight =
                    getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true)
                            ? TypedValue.complexToDimensionPixelSize(
                                    typedValue.data, getResources().getDisplayMetrics())
                            : 0;
            this.map.setPadding(0, systemBars.top + actionBarHeight, 0, systemBars.bottom);
        }

        // 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();
    }


    /** 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(
                        this,
                        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) {
                                        map.moveCamera(
                                                CameraUpdateFactory.newLatLngZoom(
                                                        new LatLng(
                                                                lastKnownLocation.getLatitude(),
                                                                lastKnownLocation.getLongitude()),
                                                        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. */
    private 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(
                        this.getApplicationContext(),
                        android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            locationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(
                    this,
                    new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }


    /** Handles the result of the request for location 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();
    }


    /**
     * UI representation of a place item to display in the selection dialog and map marker.
     *
     * <p>It is good practice to decouple the external data/backend model ({@link Place}) from the
     * UI layer. The UI should be as free of logic as possible: it receives presentation-ready data
     * (non-null coordinates, ready-to-display title and snippet strings) so that dialogs and map
     * overlays remain simple, predictable, and testable.
     */
    static class PlaceItemUiModel {
        final String title;
        final String snippet;
        final LatLng position;

        PlaceItemUiModel(String title, String snippet, LatLng position) {
            this.title = title;
            this.snippet = snippet;
            this.position = position;
        }
    }

    /**
     * Maps an external {@link Place} domain model into a display-ready {@link PlaceItemUiModel}.
     *
     * <p>The mapper encapsulates all data normalization, default value fallbacks (such as fallback
     * titles), and string formatting (such as appending third-party attributions to the address).
     * Keeping this transformation logic in the mapper prevents business and formatting logic from
     * leaking into the UI components. Returns {@code null} if essential coordinates are missing.
     */
    private PlaceItemUiModel toUiModel(Place place) {
        return toUiModel(place, getString(R.string.default_info_title));
    }

    static PlaceItemUiModel toUiModel(Place place, String defaultTitle) {
        if (place.getLocation() == null) {
            return null;
        }
        String title = place.getDisplayName() != null ? place.getDisplayName() : defaultTitle;

        StringBuilder snippet = new StringBuilder();
        if (place.getFormattedAddress() != null) {
            snippet.append(place.getFormattedAddress());
        }
        if (place.getAttributions() != null && !place.getAttributions().isEmpty()) {
            if (snippet.length() > 0) {
                snippet.append("\n");
            }
            snippet.append(String.join("\n", place.getAttributions()));
        }
        return new PlaceItemUiModel(title, snippet.toString(), place.getLocation());
    }


    /**
     * Prompts the user to select the current place from a list of likely places, and shows the
     * current place on the map - provided the user has granted location permission.
     */
    private void showCurrentPlace() {
        if (map == null) {
            return;
        }

        if (lastKnownLocation != null && "manual".equals(lastKnownLocation.getProvider())) {
            locationPermissionGranted = true;
        }

        if (locationPermissionGranted) {
            // Use fields to define the data types to return.
            List<Place.Field> placeFields =
                    Arrays.asList(
                            Place.Field.DISPLAY_NAME,
                            Place.Field.FORMATTED_ADDRESS,
                            Place.Field.LOCATION);

            // Search around the device's last-known location, falling back to the center of the
            // map if the location is not available yet.
            LatLng center =
                    lastKnownLocation != null
                            ? new LatLng(
                                    lastKnownLocation.getLatitude(),
                                    lastKnownLocation.getLongitude())
                            : map.getCameraPosition().target;
            CircularBounds searchArea = CircularBounds.newInstance(center, SEARCH_RADIUS_METERS);

            // Use the builder to create a SearchNearbyRequest that returns the closest places
            // first.
            SearchNearbyRequest request =
                    SearchNearbyRequest.builder(searchArea, placeFields)
                            .setMaxResultCount(M_MAX_ENTRIES)
                            .setRankPreference(SearchNearbyRequest.RankPreference.DISTANCE)
                            .build();

            // Get the likely places - that is, the businesses and other points of interest that
            // are closest to the device's current location.
            final Task<SearchNearbyResponse> placeResult = placesClient.searchNearby(request);
            placeResult.addOnCompleteListener(
                    new OnCompleteListener<SearchNearbyResponse>() {
                        @Override
                        public void onComplete(@NonNull Task<SearchNearbyResponse> task) {
                            if (task.isSuccessful() && task.getResult() != null) {
                                List<Place> responsePlaces = task.getResult().getPlaces();
                                int count = Math.min(responsePlaces.size(), M_MAX_ENTRIES);
                                List<PlaceItemUiModel> likelyPlaces = new ArrayList<>();
                                for (int i = 0; i < count; i++) {
                                    PlaceItemUiModel model = toUiModel(responsePlaces.get(i));
                                    if (model != null) {
                                        likelyPlaces.add(model);
                                    }
                                }

                                // Show a dialog offering the user the list of likely places, and
                                // add a marker at the selected place.
                                MapsActivityCurrentPlace.this.openPlacesDialog(likelyPlaces);
                            } else {
                                Log.e(TAG, "Exception: %s", task.getException());
                            }
                        }
                    });
        } else {
            // The user has not granted permission.
            Log.i(TAG, "The user did not grant location permission.");

            // Add a default marker, because the user hasn't selected a place.
            map.addMarker(
                    new MarkerOptions()
                            .title(getString(R.string.default_info_title))
                            .position(defaultLocation)
                            .snippet(getString(R.string.default_info_snippet)));

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


    /** Displays a form allowing the user to select a place from a list of likely places. */
    void openPlacesDialog(List<PlaceItemUiModel> likelyPlaces) {
        if (likelyPlaces.isEmpty()) {
            Toast.makeText(
                            this,
                            "No places found nearby. Long-press on the map to choose another location.",
                            Toast.LENGTH_LONG)
                    .show();
            return;
        }
        // Ask the user to choose the place where they are now.
        DialogInterface.OnClickListener listener =
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // The "which" argument contains the position of the selected item.
                        PlaceItemUiModel selectedPlace = likelyPlaces.get(which);

                        // Add a marker for the selected place, with an info window
                        // showing information about that place.
                        map.addMarker(
                                new MarkerOptions()
                                        .title(selectedPlace.title)
                                        .position(selectedPlace.position)
                                        .snippet(selectedPlace.snippet));

                        // Position the map's camera at the location of the marker.
                        map.moveCamera(
                                CameraUpdateFactory.newLatLngZoom(
                                        selectedPlace.position, DEFAULT_ZOOM));
                    }
                };

        // Display the dialog.
        String[] titles = new String[likelyPlaces.size()];
        for (int i = 0; i < likelyPlaces.size(); i++) {
            titles[i] = likelyPlaces.get(i).title;
        }
        new AlertDialog.Builder(this)
                .setTitle(R.string.pick_place)
                .setItems(titles, listener)
                .show();
    }


    /** 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;
            }
        } catch (SecurityException e) {
            Log.e("Exception: %s", e.getMessage());
        }
    }


    /** Updates the current location and searches for places around the specified coordinate. */
    void setCustomLocation(LatLng latLng) {
        Location location = new Location("manual");
        location.setLatitude(latLng.latitude);
        location.setLongitude(latLng.longitude);
        lastKnownLocation = location;
        if (map != null) {
            map.animateCamera(CameraUpdateFactory.newLatLng(latLng));
        }
        showCurrentPlace();
    }
}

    
```

View the Kotlin version of the activity:
<button type="button" class="button-red button expand-control">Show / Hide Code</button>

```
    // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.example.currentplacedetailsonmap

import android.Manifest
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
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.GoogleMap.InfoWindowAdapter
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
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.libraries.places.api.Places
import com.google.android.libraries.places.api.model.CircularBounds
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.api.net.PlacesClient
import com.google.android.libraries.places.api.net.SearchNearbyRequest

/**
 * An activity that displays a map showing the place at the device's current location.
 */
class MapsActivityCurrentPlace :
    AppCompatActivity(),
    OnMapReadyCallback {
    private var map: GoogleMap? = null
    private var cameraPosition: CameraPosition? = null
    internal var selectedMarker: Marker? = null

    // The entry point to the Places API.
    private lateinit var placesClient: PlacesClient

    // The entry point to the Fused Location Provider.
    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

    // A default location (Sydney, Australia) and default zoom to use when location permission is
    // not granted.
    private val defaultLocation = LatLng(-33.8523341, 151.2106085)
    private var locationPermissionGranted = false

    // The geographical location where the device is currently located. That is, the last-known
    // location retrieved by the Fused Location Provider or set via long press.
    internal var lastKnownLocation: Location? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

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

        // Retrieve the content view that renders the map.
        setContentView(R.layout.activity_maps)

        ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets ->
            val systemBars = insets.getInsets(
                WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout(),
            )
            val actionBarContainer = window.decorView.findViewById<View>(androidx.appcompat.R.id.action_bar_container)
            actionBarContainer?.setPadding(
                actionBarContainer.paddingLeft,
                systemBars.top,
                actionBarContainer.paddingRight,
                actionBarContainer.paddingBottom,
            )
            val typedValue = TypedValue()
            val actionBarHeight = if (theme.resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) {
                TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
            } else {
                0
            }
            map?.setPadding(0, systemBars.top + actionBarHeight, 0, systemBars.bottom)
            insets
        }
        // Construct a PlacesClient
        Places.initializeWithNewPlacesApiEnabled(applicationContext, BuildConfig.MAPS_API_KEY)
        placesClient = Places.createClient(this)

        // Construct a FusedLocationProviderClient.
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

        // Build the map.
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?
        mapFragment?.getMapAsync(this)
    }

    /**
     * Saves the state of the map when the activity is paused.
     */
    override fun onSaveInstanceState(outState: Bundle) {
        map?.let { map ->
            outState.putParcelable(KEY_CAMERA_POSITION, map.cameraPosition)
            outState.putParcelable(KEY_LOCATION, lastKnownLocation)
        }
        super.onSaveInstanceState(outState)
    }

    /**
     * Sets up the options menu.
     * @param menu The options menu.
     * @return Boolean.
     */
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.current_place_menu, menu)
        return true
    }

    /**
     * Handles a click on the menu option to get a place.
     * @param item The menu item to handle.
     * @return Boolean.
     */
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if (item.itemId == R.id.option_get_place) {
            showCurrentPlace()
        }
        return true
    }

    /**
     * Manipulates the map when it's available.
     * This callback is triggered when the map is ready to be used.
     */
    override fun onMapReady(map: GoogleMap) {
        this.map = map

        // Use a custom info window adapter to handle multiple lines of text in the
        // info window contents.
        this.map?.setInfoWindowAdapter(object : InfoWindowAdapter {
            // Return null here, so that getInfoContents() is called next.
            override fun getInfoWindow(arg0: Marker): View? = null

            override fun getInfoContents(marker: Marker): View {
                // Inflate the layouts for the info window, title and snippet.
                val infoWindow = layoutInflater.inflate(
                    R.layout.custom_info_contents,
                    findViewById<FrameLayout>(R.id.map),
                    false,
                )
                val title = infoWindow.findViewById<TextView>(R.id.title)
                title.text = marker.title
                val snippet = infoWindow.findViewById<TextView>(R.id.snippet)
                snippet.text = marker.snippet
                return infoWindow
            }
        })

        // Allow moving the current location with a long press on the map to easily
        // demo and discover nearby places anywhere in the world.
        this.map?.setOnMapLongClickListener { latLng ->
            setCustomLocation(latLng)
        }

        // Prompt the user for permission.
        getLocationPermission()

        val insets = ViewCompat.getRootWindowInsets(window.decorView)
            ?.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout())
        val topInset = insets?.top ?: 0
        val bottomInset = insets?.bottom ?: 0
        val typedValue = TypedValue()
        val actionBarHeight = if (theme.resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) {
            TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
        } else {
            0
        }
        this.map?.setPadding(0, topInset + actionBarHeight, 0, bottomInset)

        // 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()
    }

    /**
     * Gets the current location of the device, and positions the map's camera.
     */
    @SuppressLint("MissingPermission")
    private fun 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) {
                val locationResult = fusedLocationProviderClient.lastLocation
                locationResult.addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        // Set the map's camera position to the current location of the device.
                        lastKnownLocation = task.result
                        if (lastKnownLocation != null) {
                            map?.moveCamera(
                                CameraUpdateFactory.newLatLngZoom(
                                    LatLng(
                                        lastKnownLocation!!.latitude,
                                        lastKnownLocation!!.longitude,
                                    ),
                                    DEFAULT_ZOOM.toFloat(),
                                ),
                            )
                        }
                    } else {
                        Log.d(TAG, "Current location is null. Using defaults.")
                        Log.e(TAG, "Exception: %s", task.exception)
                        map?.moveCamera(
                            CameraUpdateFactory
                                .newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat()),
                        )
                        map?.uiSettings?.isMyLocationButtonEnabled = false
                    }
                }
            }
        } catch (e: SecurityException) {
            Log.e("Exception: %s", e.message, e)
        }
    }

    /**
     * Prompts the user for permission to use the device location.
     */
    private fun 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(
                this.applicationContext,
                Manifest.permission.ACCESS_FINE_LOCATION,
            )
            == PackageManager.PERMISSION_GRANTED
        ) {
            locationPermissionGranted = true
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION,
            )
        }
    }

    /**
     * Handles the result of the request for location permissions.
     */
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        locationPermissionGranted = false
        when (requestCode) {
            PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION -> {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.isNotEmpty() &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED
                ) {
                    locationPermissionGranted = true
                }
            }

            else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        }
        updateLocationUI()
    }

    private fun Place.toUiModel(): PlaceItemUiModel? = toPlaceItemUiModel(this, getString(R.string.default_info_title))

    /**
     * Prompts the user to select the current place from a list of likely places, and shows the
     * current place on the map - provided the user has granted location permission.
     */
    private fun showCurrentPlace() {
        val map = map ?: return
        if (lastKnownLocation?.provider == "manual") {
            locationPermissionGranted = true
        }
        if (locationPermissionGranted) {
            // Use fields to define the data types to return.
            val placeFields = listOf(
                Place.Field.DISPLAY_NAME,
                Place.Field.FORMATTED_ADDRESS,
                Place.Field.LOCATION,
            )

            // Search around the device's last-known location, falling back to the center of
            // the map if the location is not available yet.
            val center = lastKnownLocation?.let { LatLng(it.latitude, it.longitude) }
                ?: map.cameraPosition.target
            val searchArea = CircularBounds.newInstance(center, SEARCH_RADIUS_METERS)

            // Use the builder to create a SearchNearbyRequest that returns the closest places
            // first.
            val request = SearchNearbyRequest.builder(searchArea, placeFields)
                .setMaxResultCount(M_MAX_ENTRIES)
                .setRankPreference(SearchNearbyRequest.RankPreference.DISTANCE)
                .build()

            // Get the likely places - that is, the businesses and other points of interest that
            // are closest to the device's current location.
            val placeResult = placesClient.searchNearby(request)
            placeResult.addOnCompleteListener { task ->
                if (task.isSuccessful && task.result != null) {
                    val likelyPlaces = task.result.places
                        .take(M_MAX_ENTRIES)
                        .mapNotNull { it.toUiModel() }

                    // Show a dialog offering the user the list of likely places, and add a
                    // marker at the selected place.
                    openPlacesDialog(likelyPlaces)
                } else {
                    Log.e(TAG, "Exception: %s", task.exception)
                }
            }
        } else {
            // The user has not granted permission.
            Log.i(TAG, "The user did not grant location permission.")

            // Add a default marker, because the user hasn't selected a place.
            map.addMarker(
                MarkerOptions()
                    .title(getString(R.string.default_info_title))
                    .position(defaultLocation)
                    .snippet(getString(R.string.default_info_snippet)),
            )

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

    /**
     * Displays a form allowing the user to select a place from a list of likely places.
     */
    internal fun openPlacesDialog(likelyPlaces: List<PlaceItemUiModel>) {
        if (likelyPlaces.isEmpty()) {
            Toast.makeText(
                this,
                "No places found nearby. Long-press on the map to choose another location.",
                Toast.LENGTH_LONG,
            ).show()
            return
        }
        // Ask the user to choose the place where they are now.
        val listener = DialogInterface.OnClickListener { _, which ->
            // The "which" argument contains the position of the selected item.
            val selectedPlace = likelyPlaces[which]

            // Add a marker for the selected place, with an info window
            // showing information about that place.
            selectedMarker = map?.addMarker(
                MarkerOptions()
                    .title(selectedPlace.title)
                    .position(selectedPlace.position)
                    .snippet(selectedPlace.snippet),
            )

            // Position the map's camera at the location of the marker.
            map?.moveCamera(
                CameraUpdateFactory.newLatLngZoom(
                    selectedPlace.position,
                    DEFAULT_ZOOM.toFloat(),
                ),
            )
        }

        // Display the dialog.
        val titles = likelyPlaces.map { it.title }.toTypedArray()
        AlertDialog.Builder(this)
            .setTitle(R.string.pick_place)
            .setItems(titles, listener)
            .show()
    }

    /**
     * Updates the map's UI settings based on whether the user has granted location permission.
     */
    @SuppressLint("MissingPermission")
    private fun updateLocationUI() {
        if (map == null) {
            return
        }
        try {
            if (locationPermissionGranted) {
                map?.isMyLocationEnabled = true
                map?.uiSettings?.isMyLocationButtonEnabled = true
            } else {
                map?.isMyLocationEnabled = false
                map?.uiSettings?.isMyLocationButtonEnabled = false
                lastKnownLocation = null
                getLocationPermission()
            }
        } catch (e: SecurityException) {
            Log.e("Exception: %s", e.message, e)
        }
    }

    /**
     * Updates the current location and searches for places around the specified coordinate.
     */
    internal fun setCustomLocation(latLng: LatLng) {
        val location = Location("manual").apply {
            latitude = latLng.latitude
            longitude = latLng.longitude
        }
        lastKnownLocation = location
        map?.animateCamera(CameraUpdateFactory.newLatLng(latLng))
        showCurrentPlace()
    }

    companion object {
        private val TAG = MapsActivityCurrentPlace::class.java.simpleName
        private const val DEFAULT_ZOOM = 15
        private const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1

        // Keys for storing activity state.
        private const val KEY_CAMERA_POSITION = "camera_position"
        private const val KEY_LOCATION = "location"

        // Used for selecting the current place.
        private const val M_MAX_ENTRIES = 5
        private const val SEARCH_RADIUS_METERS = 100.0
    }
}


/**
 * UI representation of a place item to display in the selection dialog and map marker.
 *
 * It is good practice to decouple the external data/backend model ([Place]) from the UI layer.
 * The UI should be as free of logic as possible: it receives presentation-ready data (non-null
 * coordinates, ready-to-display title and snippet strings) so that dialogs and map overlays
 * remain simple, predictable, and testable.
 */
internal data class PlaceItemUiModel(val title: String, val snippet: String, val position: LatLng)

/**
 * Maps an external [Place] domain model into a display-ready [PlaceItemUiModel].
 *
 * The mapper encapsulates all data normalization, default value fallbacks (such as fallback
 * titles), and string formatting (such as appending third-party attributions to the address).
 * Keeping this transformation logic in the mapper prevents business and formatting logic
 * from leaking into the UI components. Returns `null` if essential coordinates are missing.
 */
internal fun toPlaceItemUiModel(place: Place, defaultTitle: String): PlaceItemUiModel? {
    val position = place.location ?: return null
    val title = place.displayName ?: defaultTitle
    val snippet = buildString {
        place.formattedAddress?.let { append(it) }
        place.attributions?.takeIf { it.isNotEmpty() }?.let {
            if (isNotEmpty()) append("\n")
            append(it.joinToString("\n"))
        }
    }
    return PlaceItemUiModel(title = title, snippet = snippet, position = position)
}

    
```

## Set up your development project

![](https://developers.google.com/static/maps/documentation/android-sdk/images/current-place.png)

Follow these steps to create the tutorial project in Android Studio.

1. [Download](https://developer.android.com/studio/index.html) and [install](https://developer.android.com/studio/install.html?pkg=studio) Android Studio.
2. [Add
   the **Google Play services** package](https://developer.android.com/studio/intro/update.html#sdk-manager) to Android Studio.
3. [Clone
   or download the Google Maps Android API v2 Samples repository](https://github.com/googlemaps-samples/android-samples) if you didn't do that when you started reading this tutorial.
4. Import the tutorial project:

   - In Android Studio, select **File \> New \> Import
     Project**.
   - Go to the location where you saved the Google Maps Android API v2 Samples repository after downloading it.
   - Find the **CurrentPlaceDetailsOnMap** project at this location:  
     `PATH-TO-SAVED-REPO/android-samples/tutorials/java/CurrentPlaceDetailsOnMap` (Java) or  
     `PATH-TO-SAVED-REPO/android-samples/tutorials/kotlin/CurrentPlaceDetailsOnMap` (Kotlin)
   - Select the project directory, then click **Open**. Android Studio now builds your project, using the Gradle build tool.

## Enable the necessary APIs and get an API key

To complete this tutorial, you need a Google Cloud project with the necessary
APIs enabled and an API key that's authorized to use the Maps SDK for Android.
For more details, see:

- [Set up your Google Cloud project](https://developers.google.com/maps/documentation/android-sdk/cloud-setup)
- [Use an API key](https://developers.google.com/maps/documentation/android-sdk/get-api-key.md.txt)

> [!NOTE]
> **Note:**This tutorial requires that you enable the Places API as well as the Maps SDK for Android.

To view the APIs your enabled APIs, go to the Google Maps Platform page
in the Cloud console and select your project:
[Go to Google Maps Platform page](https://console.cloud.google.com/project/_/google/maps-apis/api-list?utm_source=Docs_EnabledMapsAPIsView)

If you do not see that the Places API is enabled in your project, you must enable it:
[Enable the Places API](https://console.cloud.google.com/apis/library/places-backend.googleapis.com)

If you add any restrictions to your API key, make sure to add the Places API
to the key. See [Using API Keys](https://developers.google.com/maps/documentation/android-sdk/get-api-key.md.txt)
for more information.

## Add the API key to your app

1. Open your project's `local.properties` file.
2. Add the following string and then replace `YOUR_API_KEY` with the value of your API key:

   ```
   MAPS_API_KEY=YOUR_API_KEY
   ```

   When you build your app, the
   [Secrets Gradle Plugin for Android](https://github.com/google/secrets-gradle-plugin)
   will copy the API key and make it available as a build variable in the Android manifest.

## Build and run your app

1. Connect an Android device to your computer. Follow the
   [instructions](https://developer.android.com/studio/run/device.html)
   to enable developer options on your Android device and configure your system
   to detect the device.

   Alternatively, you can use the
   [Android
   Virtual Device (AVD) Manager](https://developer.android.com/studio/run/managing-avds.html) to configure a virtual device. When choosing an emulator,
   make sure you pick an image that includes the Google APIs. For more details, see the
   [Set Up an Android Studio Project](https://developers.google.com/maps/documentation/android-sdk/config.md.txt).
2. In Android Studio, click the **Run** menu option (or the play button icon). Choose a device as prompted.

Android Studio invokes Gradle to build the app, and then runs the app on
the device or on the emulator. You should see a map with a number of markers
centered around your current location, similar to the image on this page.

1. Select **Get Place** to open a list of places (a business or other point of interest) near your current location. To see the list of places, you must grant location permission when prompted.
2. Select a place from the list. A marker is added to the map for the selected place.

Troubleshooting:

- If you don't see a map, check that you've obtained an API key and added it to the app, [as described above](https://developers.google.com/maps/documentation/android-sdk/current-place-tutorial.md.txt#api-key). Check the log in Android Studio's [Android
  Monitor](https://developer.android.com/studio/debug/am-logcat.html) for error messages about the API key.
- If the map shows just a single marker located on Sydney Harbour Bridge (the default location specified in the app), check that you've granted **location permission** to the app. The app prompts for location permission at run time, following the pattern described in the [Android
  permissions guide](https://developer.android.com/training/permissions/requesting.html). Note that you can also set permissions directly on the device, by choosing **Settings \> Apps \> *app name* \>
  Permissions \> Location** . For details on how to handle permissions in your code, see the guide below to [requesting
  location permission in your app](https://developers.google.com/maps/documentation/android-sdk/current-place-tutorial#location-permission).
- Use the [Android
  Studio debugging tools](https://developer.android.com/studio/debug/am-logcat.html) to view logs and debug the app.

## Understand the code

This part of the tutorial explains the most significant parts of the
**CurrentPlaceDetailsOnMap** app, to help you understand how to
build a similar app.

### Instantiate the Places API client

These objects are the primary entry points to the Places SDK for Android:

- The [`Places`](https://developers.google.com/maps/documentation/places/android-sdk/reference/com/google/android/libraries/places/api/Places.md.txt) class creates and manages clients for the Places SDK for Android.
- The [`PlacesClient`](https://developers.google.com/maps/documentation/places/android-sdk/reference/com/google/android/libraries/places/api/net/PlacesClient.md.txt) interface retrieves the current location of the device and the places near the location.

The [LocationServices](https://developers.google.com/android/reference/com/google/android/gms/location/LocationServices.md.txt)
interface is the main entry point for Android location services.

To use the APIs, do the following in the
[`onCreate()`](https://developer.android.com/reference/android/app/Activity#onCreate(android.os.Bundle,%20android.os.PersistableBundle))
method of your fragment or activity:

1. Initialize a `Places` object.
2. Create a `PlacesClient` object.
3. Create a `FusedLocationProviderClient` object.


For example:

### Java

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...

    // Retrieve the content view that renders the map.
    setContentView(R.layout.activity_maps);

    // Construct a PlacesClient
    Places.initialize(getApplicationContext(), getString(R.string.maps_api_key));
    placesClient = Places.createClient(this);

    // Construct a FusedLocationProviderClient.
    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
}
```

### Kotlin

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // ...

    // Retrieve the content view that renders the map.
    setContentView(R.layout.activity_maps)

    // Construct a PlacesClient
    Places.initialize(applicationContext, getString(R.string.maps_api_key))
    placesClient = Places.createClient(this)

    // Construct a FusedLocationProviderClient.
    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

}
```

### Request location permission

Your app must request location permission in order to determine the
location of the device and to allow the user to tap the *My Location*
button on the map.

This tutorial provides the code you need to request fine location permission.
For more details, see the guide to
[Android
permissions](https://developer.android.com/training/permissions/requesting.html).

1. Add the permission as a child of the `<manifest>`
   element in your Android manifest:

   ```
   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.example.currentplacedetailsonmap">
       <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
   </manifest>
   ```
2. Request runtime permissions in your app, giving the user the
   opportunity to allow or deny location permission. The following code checks
   whether the user has granted fine location permission. If not, it requests
   the permission:

   ### Java

   ```java
   private 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(
                       this.getApplicationContext(),
                       android.Manifest.permission.ACCESS_FINE_LOCATION)
               == PackageManager.PERMISSION_GRANTED) {
           locationPermissionGranted = true;
       } else {
           ActivityCompat.requestPermissions(
                   this,
                   new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION},
                   PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
       }
   }
   ```

   ### Kotlin

   ```kotlin
   private fun 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(
               this.applicationContext,
               Manifest.permission.ACCESS_FINE_LOCATION,
           )
           == PackageManager.PERMISSION_GRANTED
       ) {
           locationPermissionGranted = true
       } else {
           ActivityCompat.requestPermissions(
               this,
               arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
               PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION,
           )
       }
   }
   ```
3. Override the `onRequestPermissionsResult()` callback to
   handle the result of the permission request:

   ### Java

   ```java
   @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();
   }
   ```

   ### Kotlin

   ```kotlin
   override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
       locationPermissionGranted = false
       when (requestCode) {
           PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION -> {
               // If request is cancelled, the result arrays are empty.
               if (grantResults.isNotEmpty() &&
                   grantResults[0] == PackageManager.PERMISSION_GRANTED
               ) {
                   locationPermissionGranted = true
               }
           }

           else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
       }
       updateLocationUI()
   }
   ```

   A later section of this tutorial describes the
   `updateLocationUI()` method.

### Add a map

Display a map, using the Maps SDK for Android.

1. Add a `<fragment>` element to your activity's layout
   file, `activity_maps.xml`. This element defines a
   [`SupportMapFragment`](https://developers.google.com/android/reference/com/google/android/gms/maps/SupportMapFragment.md.txt)
   to act as a container for the map and to provide access to the
   `GoogleMap` object. The tutorial uses the Android support library
   version of the map fragment, to ensure backward compatibility with earlier
   versions of the Android framework.

   ```
   <!--
    Copyright 2020 Google LLC

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
   -->

   <fragment xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:id="@+id/map"
       android:name="com.google.android.gms.maps.SupportMapFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       tools:context="com.example.currentplacedetailsonmap.MapsActivityCurrentPlace" />
   ```
2. In your activity's `onCreate()` method, set the layout file
   as the content view:

   ### Java

   ```java
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);


       // Retrieve the content view that renders the map.
       setContentView(R.layout.activity_maps);

   }
   ```

   ### Kotlin

   ```kotlin
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)


       // Retrieve the content view that renders the map.
       setContentView(R.layout.activity_maps)

   }
   ```
3. Implement the
   [`OnMapReadyCallback`](https://developers.google.com/android/reference/com/google/android/gms/maps/OnMapReadyCallback.md.txt)
   interface and override the `onMapReady()` method, to set up the
   map when the `GoogleMap` object is available:

   ### Java

   ```java
   @Override
   public void onMapReady(GoogleMap map) {
       this.map = map;

       // ...

       // 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();
   }
   ```

   ### Kotlin

   ```kotlin
   override fun onMapReady(map: GoogleMap) {
       this.map = map

       // ...

       // 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()
   }
   ```
4. In your activity's `onCreate()` method, get a handle to
   the map fragment by calling
   [`FragmentManager.findFragmentById()`](https://developer.android.com/reference/android/app/FragmentManager.html#findFragmentById(int)).
   Then use
   [`getMapAsync()`](https://developers.google.com/android/reference/com/google/android/gms/maps/MapFragment.md.txt#getMapAsync(com.google.android.gms.maps.OnMapReadyCallback))
   to register for the map callback:

   ### Java

   ```java
   SupportMapFragment mapFragment =
           (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
   mapFragment.getMapAsync(this);
   ```

   ### Kotlin

   ```kotlin
   val mapFragment = supportFragmentManager
       .findFragmentById(R.id.map) as SupportMapFragment?
   mapFragment?.getMapAsync(this)
   ```
5. Write an `updateLocationUI()` method to set the location
   controls on the map. If the user has granted location permission, enable the
   My Location layer and the related control on the map, otherwise disable the
   layer and the control, and set the current location to null:

   ### Java

   ```java
   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;
           }
       } catch (SecurityException e) {
           Log.e("Exception: %s", e.getMessage());
       }
   }
   ```

   ### Kotlin

   ```kotlin
   @SuppressLint("MissingPermission")
   private fun updateLocationUI() {
       if (map == null) {
           return
       }
       try {
           if (locationPermissionGranted) {
               map?.isMyLocationEnabled = true
               map?.uiSettings?.isMyLocationButtonEnabled = true
           } else {
               map?.isMyLocationEnabled = false
               map?.uiSettings?.isMyLocationButtonEnabled = false
               lastKnownLocation = null
               getLocationPermission()
           }
       } catch (e: SecurityException) {
           Log.e("Exception: %s", e.message, e)
       }
   }
   ```

### Get the location of the Android device and position the map

Use the fused location provider to find the device's last-known location,
then use that location to position the map. The tutorial provides the code you
need. For more details on getting the device's location, see the guide to the
[fused
location provider](https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi) in the Google Play services location APIs.

### Java

```java
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(
                    this,
                    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) {
                                    map.moveCamera(
                                            CameraUpdateFactory.newLatLngZoom(
                                                    new LatLng(
                                                            lastKnownLocation.getLatitude(),
                                                            lastKnownLocation.getLongitude()),
                                                    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);
    }
}
```

### Kotlin

```kotlin
@SuppressLint("MissingPermission")
private fun 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) {
            val locationResult = fusedLocationProviderClient.lastLocation
            locationResult.addOnCompleteListener(this) { task ->
                if (task.isSuccessful) {
                    // Set the map's camera position to the current location of the device.
                    lastKnownLocation = task.result
                    if (lastKnownLocation != null) {
                        map?.moveCamera(
                            CameraUpdateFactory.newLatLngZoom(
                                LatLng(
                                    lastKnownLocation!!.latitude,
                                    lastKnownLocation!!.longitude,
                                ),
                                DEFAULT_ZOOM.toFloat(),
                            ),
                        )
                    }
                } else {
                    Log.d(TAG, "Current location is null. Using defaults.")
                    Log.e(TAG, "Exception: %s", task.exception)
                    map?.moveCamera(
                        CameraUpdateFactory
                            .newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat()),
                    )
                    map?.uiSettings?.isMyLocationButtonEnabled = false
                }
            }
        }
    } catch (e: SecurityException) {
        Log.e("Exception: %s", e.message, e)
    }
}
```

### Get the current place

Use the Places SDK for Android to get a list of likely places at the
device's current location. In this context, a *place* is a business
or other point of interest.

This tutorial gets the current place when the user clicks a
**Get Place** button. It offers the user a list of likely places
to choose from, then adds a marker on the map at the location of the selected
place. The tutorial provides the code you need to interact with the
Places SDK for Android. For more details, see the guide to
[getting the current place](https://developers.google.com/maps/documentation/places/android-sdk/current-place).

1. Create a layout file (`current_place_menu.xml`) for the options menu, and override the `onCreateOptionsMenu()` method, to set up the options menu. See the accompanying sample app for the code.
2. Override the `onOptionsItemSelected()` method to get the current place when the user clicks the **Get Place** option:

   ### Java

   ```java
   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
       if (item.getItemId() == R.id.option_get_place) {
           showCurrentPlace();
       }
       return true;
   }
   ```

   ### Kotlin

   ```kotlin
   override fun onOptionsItemSelected(item: MenuItem): Boolean {
       if (item.itemId == R.id.option_get_place) {
           showCurrentPlace()
       }
       return true
   }
   ```
3. Create a `showCurrentPlace()` method to get a list of likely
   places at the device's current location:

   ### Java

   ```java
   private void showCurrentPlace() {
       if (map == null) {
           return;
       }

       // ...

       if (locationPermissionGranted) {
           // Use fields to define the data types to return.
           List<Place.Field> placeFields =
                   Arrays.asList(
                           Place.Field.DISPLAY_NAME,
                           Place.Field.FORMATTED_ADDRESS,
                           Place.Field.LOCATION);

           // Search around the device's last-known location, falling back to the center of the
           // map if the location is not available yet.
           LatLng center =
                   lastKnownLocation != null
                           ? new LatLng(
                                   lastKnownLocation.getLatitude(),
                                   lastKnownLocation.getLongitude())
                           : map.getCameraPosition().target;
           CircularBounds searchArea = CircularBounds.newInstance(center, SEARCH_RADIUS_METERS);

           // Use the builder to create a SearchNearbyRequest that returns the closest places
           // first.
           SearchNearbyRequest request =
                   SearchNearbyRequest.builder(searchArea, placeFields)
                           .setMaxResultCount(M_MAX_ENTRIES)
                           .setRankPreference(SearchNearbyRequest.RankPreference.DISTANCE)
                           .build();

           // Get the likely places - that is, the businesses and other points of interest that
           // are closest to the device's current location.
           final Task<SearchNearbyResponse> placeResult = placesClient.searchNearby(request);
           placeResult.addOnCompleteListener(
                   new OnCompleteListener<SearchNearbyResponse>() {
                       @Override
                       public void onComplete(@NonNull Task<SearchNearbyResponse> task) {
                           if (task.isSuccessful() && task.getResult() != null) {
                               List<Place> responsePlaces = task.getResult().getPlaces();
                               int count = Math.min(responsePlaces.size(), M_MAX_ENTRIES);
                               List<PlaceItemUiModel> likelyPlaces = new ArrayList<>();
                               for (int i = 0; i < count; i++) {
                                   PlaceItemUiModel model = toUiModel(responsePlaces.get(i));
                                   if (model != null) {
                                       likelyPlaces.add(model);
                                   }
                               }

                               // Show a dialog offering the user the list of likely places, and
                               // add a marker at the selected place.
                               MapsActivityCurrentPlace.this.openPlacesDialog(likelyPlaces);
                           } else {
                               Log.e(TAG, "Exception: %s", task.getException());
                           }
                       }
                   });
       } else {
           // The user has not granted permission.
           Log.i(TAG, "The user did not grant location permission.");

           // Add a default marker, because the user hasn't selected a place.
           map.addMarker(
                   new MarkerOptions()
                           .title(getString(R.string.default_info_title))
                           .position(defaultLocation)
                           .snippet(getString(R.string.default_info_snippet)));

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

   ### Kotlin

   ```kotlin
   private fun showCurrentPlace() {
       val map = map ?: return
       // ...
       if (locationPermissionGranted) {
           // Use fields to define the data types to return.
           val placeFields = listOf(
               Place.Field.DISPLAY_NAME,
               Place.Field.FORMATTED_ADDRESS,
               Place.Field.LOCATION,
           )

           // Search around the device's last-known location, falling back to the center of
           // the map if the location is not available yet.
           val center = lastKnownLocation?.let { LatLng(it.latitude, it.longitude) }
               ?: map.cameraPosition.target
           val searchArea = CircularBounds.newInstance(center, SEARCH_RADIUS_METERS)

           // Use the builder to create a SearchNearbyRequest that returns the closest places
           // first.
           val request = SearchNearbyRequest.builder(searchArea, placeFields)
               .setMaxResultCount(M_MAX_ENTRIES)
               .setRankPreference(SearchNearbyRequest.RankPreference.DISTANCE)
               .build()

           // Get the likely places - that is, the businesses and other points of interest that
           // are closest to the device's current location.
           val placeResult = placesClient.searchNearby(request)
           placeResult.addOnCompleteListener { task ->
               if (task.isSuccessful && task.result != null) {
                   val likelyPlaces = task.result.places
                       .take(M_MAX_ENTRIES)
                       .mapNotNull { it.toUiModel() }

                   // Show a dialog offering the user the list of likely places, and add a
                   // marker at the selected place.
                   openPlacesDialog(likelyPlaces)
               } else {
                   Log.e(TAG, "Exception: %s", task.exception)
               }
           }
       } else {
           // The user has not granted permission.
           Log.i(TAG, "The user did not grant location permission.")

           // Add a default marker, because the user hasn't selected a place.
           map.addMarker(
               MarkerOptions()
                   .title(getString(R.string.default_info_title))
                   .position(defaultLocation)
                   .snippet(getString(R.string.default_info_snippet)),
           )

           // Prompt the user for permission.
           getLocationPermission()
       }
   }
   ```
4. Create an `openPlacesDialog()` method to display a form
   allowing the user to select a place from a list of likely places. Add a
   marker on the map for the selected place. The marker content includes the
   name and address of the place, and any attributions that the API supplies:

   ### Java

   ```java
   void openPlacesDialog(List<PlaceItemUiModel> likelyPlaces) {
       // ...
       // Ask the user to choose the place where they are now.
       DialogInterface.OnClickListener listener =
               new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       // The "which" argument contains the position of the selected item.
                       PlaceItemUiModel selectedPlace = likelyPlaces.get(which);

                       // Add a marker for the selected place, with an info window
                       // showing information about that place.
                       map.addMarker(
                               new MarkerOptions()
                                       .title(selectedPlace.title)
                                       .position(selectedPlace.position)
                                       .snippet(selectedPlace.snippet));

                       // Position the map's camera at the location of the marker.
                       map.moveCamera(
                               CameraUpdateFactory.newLatLngZoom(
                                       selectedPlace.position, DEFAULT_ZOOM));
                   }
               };

       // Display the dialog.
       String[] titles = new String[likelyPlaces.size()];
       for (int i = 0; i < likelyPlaces.size(); i++) {
           titles[i] = likelyPlaces.get(i).title;
       }
       new AlertDialog.Builder(this)
               .setTitle(R.string.pick_place)
               .setItems(titles, listener)
               .show();
   }
   ```

   ### Kotlin

   ```kotlin
   internal fun openPlacesDialog(likelyPlaces: List<PlaceItemUiModel>) {
       // ...
       // Ask the user to choose the place where they are now.
       val listener = DialogInterface.OnClickListener { _, which ->
           // The "which" argument contains the position of the selected item.
           val selectedPlace = likelyPlaces[which]

           // Add a marker for the selected place, with an info window
           // showing information about that place.
           selectedMarker = map?.addMarker(
               MarkerOptions()
                   .title(selectedPlace.title)
                   .position(selectedPlace.position)
                   .snippet(selectedPlace.snippet),
           )

           // Position the map's camera at the location of the marker.
           map?.moveCamera(
               CameraUpdateFactory.newLatLngZoom(
                   selectedPlace.position,
                   DEFAULT_ZOOM.toFloat(),
               ),
           )
       }

       // Display the dialog.
       val titles = likelyPlaces.map { it.title }.toTypedArray()
       AlertDialog.Builder(this)
           .setTitle(R.string.pick_place)
           .setItems(titles, listener)
           .show()
   }
   ```
5. Create a custom layout for the info window content. This makes it
   possible to display multiple lines of content in the info window. First, add
   an XML layout file, `custom_info_contents.xml`, containing a text
   view for the info window title, and another text view for the snippet (that
   is, the info window's textual content):

   ```
   <?xml version="1.0" encoding="utf-8"?>
   <!--
    Copyright 2020 Google LLC

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
   -->

   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layoutDirection="locale"
       android:orientation="vertical">
       <TextView
           android:id="@+id/title"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center_horizontal"
           android:textColor="#ff000000"
           android:textStyle="bold" />

       <TextView
           android:id="@+id/snippet"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:textColor="#ff7f7f7f" />
   </LinearLayout>
   ```
6. Implement the
   [`InfoWindowAdapter`](https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter.md.txt)
   interface to inflate the layout and load the info window content:

   ### Java

   ```java
   // Use a custom info window adapter to handle multiple lines of text in the
   // info window contents.
   this.map.setInfoWindowAdapter(
           new GoogleMap.InfoWindowAdapter() {

               @Override
               // 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,
                                           (FrameLayout) 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;
               }
           });
   ```

   ### Kotlin

   ```kotlin
   // Use a custom info window adapter to handle multiple lines of text in the
   // info window contents.
   this.map?.setInfoWindowAdapter(object : InfoWindowAdapter {
       // Return null here, so that getInfoContents() is called next.
       override fun getInfoWindow(arg0: Marker): View? = null

       override fun getInfoContents(marker: Marker): View {
           // Inflate the layouts for the info window, title and snippet.
           val infoWindow = layoutInflater.inflate(
               R.layout.custom_info_contents,
               findViewById<FrameLayout>(R.id.map),
               false,
           )
           val title = infoWindow.findViewById<TextView>(R.id.title)
           title.text = marker.title
           val snippet = infoWindow.findViewById<TextView>(R.id.snippet)
           snippet.text = marker.snippet
           return infoWindow
       }
   })
   ```

> [!NOTE]
> **Note:** By default, the Maps SDK for Android displays the content of the info window when the user taps a marker. There's no need to add a click listener for the marker if you're happy to use the default behavior.

### Save the map's state

Save the map's camera position and the device location. When a user rotates
an Android device, or makes configuration changes, the Android framework
destroys and rebuilds the map activity. To ensure a smooth user experience,
it's good to store relevant application state and restore it when needed.

This tutorial provides all the code you need to save the map's state. For
more details, see the guide to the
[`savedInstanceState`
bundle](https://developer.android.com/training/basics/activity-lifecycle/recreating.html).

1. In your map activity, set up key values for storing activity state:

   ### Java

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

   ### Kotlin

   ```kotlin
   private const val KEY_CAMERA_POSITION = "camera_position"
   private const val KEY_LOCATION = "location"
   ```
2. Implement the `onSaveInstanceState()` callback to save the
   state when the activity pauses:

   ### Java

   ```java
   @Override
   protected void onSaveInstanceState(Bundle outState) {
       if (map != null) {
           outState.putParcelable(KEY_CAMERA_POSITION, map.getCameraPosition());
           outState.putParcelable(KEY_LOCATION, lastKnownLocation);
       }
       super.onSaveInstanceState(outState);
   }
   ```

   ### Kotlin

   ```kotlin
   override fun onSaveInstanceState(outState: Bundle) {
       map?.let { map ->
           outState.putParcelable(KEY_CAMERA_POSITION, map.cameraPosition)
           outState.putParcelable(KEY_LOCATION, lastKnownLocation)
       }
       super.onSaveInstanceState(outState)
   }
   ```
3. In your activity's `onCreate()` method, retrieve the
   device's location and the map's camera position if previously saved:

   ### Java

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

   ### Kotlin

   ```kotlin
   if (savedInstanceState != null) {
       lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
       cameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION)
   }
   ```

> [!NOTE]
> **Congratulations!** You've built an Android app that shows the current place on a Google map. You've also learned how to use the [Maps SDK for Android](https://developers.google.com/maps/documentation/android-sdk), the [Places SDK for Android](https://developers.google.com/maps/documentation/places/android-sdk), and the [fused
> location provider](https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi).