main 871c70b1b9f2 cached
282 files
1005.3 KB
230.0k tokens
207 symbols
1 requests
Download .txt
Showing preview only (1,097K chars total). Download the full file or copy to clipboard to get everything.
Repository: googlemaps/android-places-demos
Branch: main
Commit: 871c70b1b9f2
Files: 282
Total size: 1005.3 KB

Directory structure:
gitextract_jubtpusk/

├── .gemini/
│   ├── config.yaml
│   ├── skills/
│   │   └── places-android/
│   │       └── SKILL.md
│   └── styleguide.md
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── support_request.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── pull_request_template.md
│   ├── dependabot.yml
│   ├── header-checker-lint.yml
│   ├── pull_request_template.md
│   ├── snippet-bot.yml
│   ├── stale.yml
│   ├── sync-repo-settings.yaml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── PlaceDetailsCompose/
│   ├── .gitignore
│   ├── ARCHITECTURE.md
│   ├── README.md
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placedetailscompose/
│           │               ├── MainActivity.kt
│           │               ├── PlaceDetailsComposeApplication.kt
│           │               ├── repository/
│           │               │   └── LocationRepository.kt
│           │               ├── ui/
│           │               │   ├── map/
│           │               │   │   ├── MapScreen.kt
│           │               │   │   ├── PlaceContentSelectionDialog.kt
│           │               │   │   └── PlaceDetailsView.kt
│           │               │   └── theme/
│           │               │       ├── Color.kt
│           │               │       ├── Theme.kt
│           │               │       ├── Typography.kt
│           │               │       └── Utils.kt
│           │               └── viewmodels/
│           │                   └── MapViewModel.kt
│           └── res/
│               ├── drawable/
│               │   ├── close_button_background.xml
│               │   ├── ic_close.xml
│               │   ├── ic_launcher_background.xml
│               │   ├── ic_launcher_foreground.xml
│               │   ├── outline_my_location_24.xml
│               │   └── outline_settings_24.xml
│               ├── layout/
│               │   └── place_details_fragment.xml
│               ├── mipmap-anydpi/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               ├── values/
│               │   ├── ids.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── xml/
│                   ├── backup_rules.xml
│                   └── data_extraction_rules.xml
├── PlaceDetailsUIKit/
│   ├── .gitignore
│   ├── README.md
│   ├── build.gradle.kts
│   ├── lint.xml
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── example/
│       │               └── placedetailsuikit/
│       │                   ├── MainActivityInstrumentedTest.kt
│       │                   ├── compact/
│       │                   │   └── ConfigurablePlaceDetailsActivityInstrumentedTest.kt
│       │                   └── full/
│       │                       └── FullConfigurablePlaceDetailsActivityInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── example/
│       │   │           └── placedetailsuikit/
│       │   │               ├── LauncherActivity.kt
│       │   │               ├── MainActivity.kt
│       │   │               ├── compact/
│       │   │               │   ├── ConfigurablePlaceDetailsActivity.kt
│       │   │               │   └── ContentSelectionViewModel.kt
│       │   │               ├── full/
│       │   │               │   ├── FullConfigurablePlaceDetailsActivity.kt
│       │   │               │   └── FullContentSelectionViewModel.kt
│       │   │               └── ui/
│       │   │                   └── theme/
│       │   │                       ├── Color.kt
│       │   │                       ├── Theme.kt
│       │   │                       └── Type.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── close_button_background.xml
│       │       │   ├── ic_close.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── ic_launcher_foreground.xml
│       │       │   ├── outline_my_location_24.xml
│       │       │   └── outline_settings_24.xml
│       │       ├── font/
│       │       │   └── custom_font.xml
│       │       ├── layout/
│       │       │   ├── activity_configurable_map.xml
│       │       │   ├── activity_full_configurable_map.xml
│       │       │   ├── activity_main.xml
│       │       │   └── content_selector_dialog.xml
│       │       ├── mipmap-anydpi/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── strings.xml
│       │       │   └── themes.xml
│       │       ├── values-night/
│       │       │   └── themes.xml
│       │       └── xml/
│       │           ├── backup_rules.xml
│       │           └── data_extraction_rules.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── example/
│                       └── placedetailsuikit/
│                           └── ExampleUnitTest.kt
├── PlacesUIKit3D/
│   ├── .gitignore
│   ├── README.md
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placesuikit3d/
│           │               ├── Landmark.kt
│           │               ├── LandmarkList.kt
│           │               ├── MainActivity.kt
│           │               ├── MainViewModel.kt
│           │               ├── Maps3DPlacesApplication.kt
│           │               ├── common/
│           │               │   ├── ActiveMapObject.kt
│           │               │   ├── Map3dViewModel.kt
│           │               │   └── MapObject.kt
│           │               ├── ui/
│           │               │   └── theme/
│           │               │       ├── Color.kt
│           │               │       ├── Theme.kt
│           │               │       └── Type.kt
│           │               └── utils/
│           │                   ├── CameraUpdate.kt
│           │                   ├── Units.kt
│           │                   └── Utilities.kt
│           └── res/
│               ├── drawable/
│               │   ├── close_button_background.xml
│               │   ├── ic_close.xml
│               │   ├── ic_launcher_background.xml
│               │   ├── ic_launcher_foreground.xml
│               │   ├── ic_my_location.xml
│               │   ├── loader_background.xml
│               │   └── outline_my_location_24.xml
│               ├── font/
│               │   └── custom_font.xml
│               ├── layout/
│               │   └── activity_main.xml
│               ├── mipmap-anydpi/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               ├── values/
│               │   ├── colors.xml
│               │   ├── strings.xml
│               │   └── themes.xml
│               └── xml/
│                   ├── backup_rules.xml
│                   └── data_extraction_rules.xml
├── README.md
├── SECURITY.md
├── build-logic/
│   ├── convention/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── main/
│   │           └── kotlin/
│   │               ├── places-demo.android.application.gradle.kts
│   │               └── places-demo.secrets.gradle.kts
│   └── settings.gradle.kts
├── build.gradle.kts
├── demo-java/
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placesdemo/
│           │               ├── AutocompleteAddressActivity.java
│           │               ├── CurrentPlaceActivity.java
│           │               ├── FieldSelector.java
│           │               ├── MainActivity.java
│           │               ├── PlaceAutocompleteActivity.java
│           │               ├── PlaceDetailsAndPhotosActivity.java
│           │               ├── PlaceIsOpenActivity.java
│           │               ├── PlacesDemoApplication.java
│           │               ├── StringUtil.java
│           │               ├── model/
│           │               │   ├── AddressType.java
│           │               │   ├── AutocompleteEditText.java
│           │               │   ├── Bounds.java
│           │               │   ├── GeocodingResult.java
│           │               │   ├── Geometry.java
│           │               │   ├── LocationType.java
│           │               │   └── PlusCode.java
│           │               └── programmatic_autocomplete/
│           │                   ├── LatLngAdapter.java
│           │                   ├── PlacePredictionAdapter.java
│           │                   └── ProgrammaticAutocompleteToolbarActivity.java
│           └── res/
│               ├── drawable/
│               │   └── ic_search_black_24dp.xml
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── activity_programmatic_autocomplete.xml
│               │   ├── autocomplete_address_activity.xml
│               │   ├── autocomplete_address_map.xml
│               │   ├── current_place_activity.xml
│               │   ├── place_autocomplete_activity.xml
│               │   ├── place_details_and_photos_activity.xml
│               │   ├── place_is_open_activity.xml
│               │   └── place_prediction_item.xml
│               ├── menu/
│               │   └── menu.xml
│               ├── raw/
│               │   └── style_json.json
│               └── values/
│                   ├── dimens.xml
│                   └── strings.xml
├── demo-kotlin/
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placesdemo/
│           │               ├── AutocompleteAddressActivity.kt
│           │               ├── BaseActivity.kt
│           │               ├── CurrentPlaceActivity.kt
│           │               ├── FieldSelector.kt
│           │               ├── MainActivity.kt
│           │               ├── PlaceAutocompleteActivity.kt
│           │               ├── PlaceDetailsAndPhotosActivity.kt
│           │               ├── PlaceIsOpenActivity.kt
│           │               ├── PlacesDemoApplication.kt
│           │               ├── PlacesDemoGlideModule.kt
│           │               ├── StringUtil.kt
│           │               ├── model/
│           │               │   ├── AddressType.kt
│           │               │   ├── AutocompleteEditText.kt
│           │               │   ├── Bounds.kt
│           │               │   ├── GeocodingResult.kt
│           │               │   ├── Geometry.kt
│           │               │   ├── LocationType.kt
│           │               │   └── PlusCode.kt
│           │               └── programmatic_autocomplete/
│           │                   ├── LatLngAdapter.kt
│           │                   ├── PlacePredictionAdapter.kt
│           │                   └── ProgrammaticAutocompleteGeocodingActivity.kt
│           └── res/
│               ├── drawable/
│               │   ├── ic_exit.xml
│               │   ├── ic_exit_to_app_black_24dp.xml
│               │   └── ic_search_black_24dp.xml
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── activity_programmatic_autocomplete.xml
│               │   ├── autocomplete_address_activity.xml
│               │   ├── autocomplete_address_map.xml
│               │   ├── current_place_activity.xml
│               │   ├── place_autocomplete_activity.xml
│               │   ├── place_details_and_photos_activity.xml
│               │   ├── place_is_open_activity.xml
│               │   └── place_prediction_item.xml
│               ├── menu/
│               │   ├── main_activity_menu.xml
│               │   └── menu.xml
│               ├── raw/
│               │   └── style_json.json
│               └── values/
│                   ├── dimens.xml
│                   └── strings.xml
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── kotlin-demos/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── google/
│           │           └── places/
│           │               └── android/
│           │                   └── ktx/
│           │                       └── demo/
│           │                           ├── AutocompleteDemoActivity.kt
│           │                           ├── Demo.kt
│           │                           ├── DemoActivity.kt
│           │                           ├── DemoApplication.kt
│           │                           ├── PlacesPhotoDemoActivity.kt
│           │                           ├── PlacesPhotoViewModel.kt
│           │                           ├── PlacesSearchDemoActivity.kt
│           │                           ├── PlacesSearchEvent.kt
│           │                           ├── PlacesSearchViewModel.kt
│           │                           ├── inject/
│           │                           │   └── DemoModule.kt
│           │                           └── ui/
│           │                               ├── Color.kt
│           │                               ├── Theme.kt
│           │                               └── Type.kt
│           └── res/
│               ├── drawable/
│               │   ├── ic_launcher_background.xml
│               │   └── ic_launcher_foreground.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── local.defaults.properties
├── settings.gradle.kts
└── snippets/
    ├── .gitignore
    ├── build.gradle.kts
    ├── local.defaults.properties
    ├── proguard-rules.pro
    └── src/
        └── main/
            ├── AndroidManifest.xml
            ├── java/
            │   └── com/
            │       └── google/
            │           └── places/
            │               ├── CurrentPlaceActivity.java
            │               ├── GetStartedActivity.java
            │               ├── JavaMainActivity.java
            │               ├── PlaceAutocompleteActivity.java
            │               ├── PlaceDetailsActivity.java
            │               ├── PlaceIsOpenActivity.java
            │               ├── PlacePhotosActivity.java
            │               ├── PlacesIconActivity.java
            │               ├── data/
            │               │   └── PlaceIdProvider.java
            │               └── kotlin/
            │                   ├── CurrentPlaceActivity.kt
            │                   ├── GetStartedActivity.kt
            │                   ├── KotlinMainActivity.kt
            │                   ├── MainApplication.kt
            │                   ├── PlaceAutocompleteActivity.kt
            │                   ├── PlaceDetailsActivity.kt
            │                   ├── PlaceIsOpenActivity.kt
            │                   ├── PlacePhotosActivity.kt
            │                   └── PlacesIconActivity.kt
            └── res/
                ├── drawable/
                │   └── ic_launcher_background.xml
                ├── drawable-v24/
                │   └── ic_launcher_foreground.xml
                ├── layout/
                │   ├── activity_current_place.xml
                │   ├── activity_main.xml
                │   ├── activity_place_autocomplete.xml
                │   ├── activity_place_details.xml
                │   ├── activity_place_is_open.xml
                │   ├── activity_place_photos.xml
                │   ├── activity_places_icon.xml
                │   ├── list_item_activity.xml
                │   └── list_item_place.xml
                ├── mipmap-anydpi-v26/
                │   ├── ic_launcher.xml
                │   └── ic_launcher_round.xml
                ├── values/
                │   ├── colors.xml
                │   ├── strings.xml
                │   └── styles.xml
                └── values-v27/
                    └── styles.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gemini/config.yaml
================================================
# Copyright 2026 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.

# Gemini Code Assist Configuration
# See: https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github

# Feature settings
have_fun: false

code_review:
  disable: false
  comment_severity_threshold: MEDIUM
  max_review_comments: -1

pull_request_opened:
  summary: true
  code_review: true
  include_drafts: true

# Files to ignore in Gemini analysis
ignore_patterns:
  - "**/*.bin"
  - "**/*.exe"
  - "**/build/**"
  - "**/.gradle/**"
  - "**/secrets.properties"


================================================
FILE: .gemini/skills/places-android/SKILL.md
================================================
---
name: places-android
description: Guide for integrating the Places SDK for Android into an application. Use when users ask to add Places, autocomplete, place details, or search for places.
---

# Places SDK for Android Integration

You are an expert Android developer specializing in modern Android architecture. Before generating any code, ask the user the following questions to tailor the solution:

### 📋 Design & Architectural Questions to Ask the User

*   **UI Framework**: Are you using Jetpack Compose or standard UI Views?
*   **Widget vs Custom UI**: Do you want to use the pre-built Google Autocomplete Widget (Dialog/Overlay) or build a completely custom programmatic search bar?
*   **Compact vs Full Details**: Do you want a compact half-sheet overlay (`PlaceDetailsCompactFragment`) or a full-page details viewer (`PlaceDetailsFragment`)?
*   **Cost & Field Scoping**: What exact fields do you need (e.g., `DISPLAY_NAME`, `FORMATTED_ADDRESS`, `PHOTO_METADATAS`)? Limiting fields saves costs!
*   **Theming Options**: Are you using Material 3 themes so we can bridge default styling automatically?

---

## 1. Setup Dependencies

Add the necessary dependencies to your module-level `build.gradle.kts` file. It is recommended to use the Versions Catalog if available:

```toml
[versions]
places = "5.1.1" # x-release-please-version

[libraries]
places = { group = "com.google.android.libraries.places", name = "places", version.ref = "places" }
```

Then in `build.gradle.kts`:

```kotlin
dependencies {
    implementation(libs.places)
}
```

## 2. Setup the Secrets Gradle Plugin

Use the Secrets Gradle Plugin for Android to inject the API key securely into your project (e.g., via `BuildConfig`), so you can access it programmatically during initialization.

Ensure you have the plugin applied in your app-level `build.gradle.kts`:

```kotlin
plugins {
    alias(libs.plugins.secrets.gradle.plugin)
}

secrets {
    propertiesFileName = "secrets.properties"
    defaultPropertiesFileName = "local.defaults.properties"
}
```

Add your API Key to `secrets.properties`:

```properties
PLACES_API_KEY=YOUR_API_KEY
```

## 3. Initialize the Places SDK

In your `Application` or `Activity` (before accessing any Places APIs, usually inside `onCreate`), initialize the Places SDK.

### Kotlin

```kotlin
import com.google.android.libraries.places.api.Places

class DemoApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        val apiKey = BuildConfig.PLACES_API_KEY
        if (apiKey.isNotEmpty()) {
            Places.initializeWithNewPlacesApiEnabled(applicationContext, apiKey)
        }
    }
}
```

### Java

```java
import com.google.android.libraries.places.api.Places;

public class DemoApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        String apiKey = BuildConfig.PLACES_API_KEY;
        if (!apiKey.isEmpty()) {
            Places.initializeWithNewPlacesApiEnabled(getApplicationContext(), apiKey);
        }
    }
}
```

## 4. Best Practices & Guidelines

*   **Prefer Places UI Kit**: For displaying place details (photos, reviews, addresses), prefer using the **Places UI Kit** over manual programmatic retrieval. It provides pre-built, beautifully designed, and automatically maintained UI components!
*   **Null Safety & Validation**: Handle nulls defensively for optional parameters (e.g. Place fields).
*   **Scoped Fields**: Always specify *only* parameters that are needed (e.g. `Place.Field.ID`, `Place.Field.DISPLAY_NAME`) to avoid over-billing.
*   **Coroutine Extensions**: Use Kotlin Coroutines extensions (`places-ktx` if available) to make code cleaner.
*   **Location Permission**: Location permissions are optional but helpful. `ACCESS_COARSE_LOCATION` is sufficient for biasing prediction calls (like searching search results) to general cities. `ACCESS_FINE_LOCATION` is necessary only for exact current position tracking. Declare them in your `AndroidManifest.xml`:

    ```xml
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    </manifest>
    ```

## 5. Compose Interop with Places UI Kit

The Places UI Kit (`PlaceDetailsCompactFragment` and `PlaceDetailsFragment`) are View-based. To use them in Jetpack Compose, use `AndroidView` to host a `FragmentContainerView`.

### Key Pattern: Fragment Container in Compose

*   **Access FragmentManager**: Use standard `LocalActivity.current as FragmentActivity` to access the support FragmentManager. Avoid casting `LocalContext.current` directly to Activity.
*   **Deferred Updates**: Inside the `AndroidView` `update` block, always wrap calls (like `.loadWithPlaceId()`) in `view.post { ... }` to ensure updates run *after* the layout is inflated and bindings are stable.

```kotlin
@Composable
fun PlaceDetailsCompactView(
    placeId: String,
    onDismiss: () -> Unit
) {
    val fragmentContainerId = remember { View.generateViewId() }
    val fragmentManager = (LocalActivity.current as FragmentActivity).supportFragmentManager

    val fragment = remember {
        PlaceDetailsCompactFragment.newInstance(
            PlaceDetailsCompactFragment.ALL_CONTENT,
            Orientation.VERTICAL
        )
    }

    Box(modifier = Modifier.fillMaxWidth()) {
        AndroidView(
            modifier = Modifier.fillMaxWidth(),
            factory = { context ->
                FragmentContainerView(context).apply {
                    id = fragmentContainerId
                    if (fragmentManager.findFragmentById(fragmentContainerId) == null) {
                        fragmentManager.beginTransaction()
                            .add(fragmentContainerId, fragment)
                            .commit()
                    }
                }
            },
            update = { view ->
                // Ensures updates run after view hierarchy is ready
                view.post {
                    fragment.loadWithPlaceId(placeId)
                }
            }
        )
    }
}
```

## 📏 6. Advanced Compose Viewports & BottomSheetScaffold

When hosting UI Kit fragments inside navigation drawers or overlays, follow these architectural bounds to avoid viewport clipping snags:

*   **System Viewport Edge Overlap**: If using `enableEdgeToEdge()` and your container loses standard `Scaffold` body padding context, manually append `Modifier.statusBarsPadding()` to avoid overlapping with system status bar text:
    ```kotlin
    Column(modifier = modifier.statusBarsPadding()) { 
        // Beautiful search results sit safely under the status bar
    }
    ```
*   **Unified Sheet Content State**: To achieve clean mutual exclusivity between "Compact" and "Full" details click toggles, hoist the viewport state to a high enum variable level:
    ```kotlin
    enum class DetailsUiType { COMPACT, FULL }
    ```
    Track `currentUiType` at the Activity level and pass it to a single shared `BottomSheetScaffold`. Users can swipe to dismiss natively without custom button overrides!

## 7. Autocomplete with Compose (Widget)

To implement autocomplete in Compose, use `ActivityResultContracts.StartActivityForResult` with an Intent from `Autocomplete.IntentBuilder`. This is the recommended way to use the pre-built widget, as it handles session tokens and debouncing automatically.

```kotlin
@Composable
fun AutocompleteSearchButton() {
    val context = LocalContext.current
    
    val fields = listOf(Place.Field.ID, Place.Field.DISPLAY_NAME, Place.Field.FORMATTED_ADDRESS)
    val intent = Autocomplete.IntentBuilder(AutocompleteActivityMode.OVERLAY, fields)
        .build(context)

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val place = Autocomplete.getPlaceFromIntent(result.data!!)
            Log.d("Autocomplete", "Place selected: ${place.name}")
        }
    }

    Button(onClick = { launcher.launch(intent) }) {
        Text("Search Places")
    }
}
```

## 8. Execution Steps

1. Add the Places SDK dependencies to `build.gradle.kts`.
2. Set up the Secrets Gradle Plugin in `build.gradle.kts`.
3. Implement initialization (e.g., in a subclass of `Application`).
4. Provide a summary of how to use it (retrieve place details, display autocomplete).


================================================
FILE: .gemini/styleguide.md
================================================
# Gemini Code Assist Style Guide: android-places-demos

This guide defines the custom code review and generation rules for the `android-places-demos` project.

## Jetpack Compose Guidelines
- **API Guidelines**: Strictly follow the [Jetpack Compose API guidelines](https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md).
- **Naming**: Composable functions must be PascalCase.
- **Modifiers**: The first optional parameter of any Composable should be `modifier: Modifier = Modifier`.

## Kotlin & Java Style
- **Naming**: Use camelCase for variables and functions.
- **Documentation**: Provide KDoc for all public classes, properties, and functions. Explain the "why" in comments, not just the "what".
- **Safety**: Use null-safe operators and avoid `!!` in Kotlin. In Java, use standard null checks or `@NonNull`/`@Nullable` annotations if available.
- **Imports vs FQCNs**: Avoid using Fully Qualified Class Names (FQCNs) in code if a standard `import` statement would suffice. Keep code readable.

## Places SDK Specifics
- **Secrets**: Never commit API keys. Ensure they are read from `secrets.properties` (or `local.properties`) via `BuildConfig` or injected into `AndroidManifest.xml` by the Secrets Gradle Plugin.
- **Initialization**:
  - Strongly recommend `Places.initializeWithNewPlacesApiEnabled` over the legacy `Places.initialize` for modern demos.
- **Literate Programming**: Write clear, well-documented code that functions as an example for developers. Explain architectural decisions.


================================================
FILE: .github/CODEOWNERS
================================================
# Copyright 2022 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.

# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners

.github/ @googlemaps/admin


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'type: bug, triage me'
assignees: ''

---

Thanks for stopping by to let us know something could be better!

---
**PLEASE READ** 

If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. 

Discover additional support services for the Google Maps Platform, including developer communities, technical guidance, and expert support at the Google Maps Platform [support resources page](https://developers.google.com/maps/support/). 

If your bug or feature request is not related to this particular library, please visit the Google Maps Platform [issue trackers](https://developers.google.com/maps/support/#issue_tracker).

Check for answers on StackOverflow with the [google-maps](http://stackoverflow.com/questions/tagged/google-maps) tag.

---

Please be sure to include as much information as possible:

#### Environment details

1. Specify the API at the beginning of the title (for example, "Places: ...")
2. OS type and version
3. Library version and other environment information

#### Steps to reproduce

  1. ?

#### Code example

```
# example
```

#### Stack trace
```
# example
```

Following these steps will guarantee the quickest resolution possible.

Thanks!


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this library
title: ''
labels: 'type: feature request, triage me'
assignees: ''

---

Thanks for stopping by to let us know something could be better!

---
**PLEASE READ** 

If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. 

Discover additional support services for the Google Maps Platform, including developer communities, technical guidance, and expert support at the Google Maps Platform [support resources page](https://developers.google.com/maps/support/). 

If your bug or feature request is not related to this particular library, please visit the Google Maps Platform [issue trackers](https://developers.google.com/maps/support/#issue_tracker).

Check for answers on StackOverflow with the [google-maps](http://stackoverflow.com/questions/tagged/google-maps) tag.

---

 **Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

 **Describe the solution you'd like**
A clear and concise description of what you want to happen.

 **Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

 **Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/ISSUE_TEMPLATE/support_request.md
================================================
---
name: Support request
about: If you have a support contract with Google, please create an issue in the Google
  Cloud Support console.
title: ''
labels: 'triage me, type: question'
assignees: ''

---

**PLEASE READ** 

If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. 

Discover additional support services for the Google Maps Platform, including developer communities, technical guidance, and expert support at the Google Maps Platform [support resources page](https://developers.google.com/maps/support/). 

If your bug or feature request is not related to this particular library, please visit the Google Maps Platform [issue trackers](https://developers.google.com/maps/support/#issue_tracker).

Check for answers on StackOverflow with the [google-maps](http://stackoverflow.com/questions/tagged/google-maps) tag.

---


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/pull_request_template.md
================================================
---
name: Pull request
about: Create a pull request
label: 'triage me'
---
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [ ] Make sure to open a GitHub issue as a bug/feature request before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> 🦕


================================================
FILE: .github/dependabot.yml
================================================
# Copyright 2025 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.

version: 2
updates:
- package-ecosystem: gradle
  directory: "/"
  schedule:
    interval: "weekly"
  open-pull-requests-limit: 10
  commit-message:
    prefix: chore(deps)


================================================
FILE: .github/header-checker-lint.yml
================================================
# Copyright 2025 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.

# Presubmit test that ensures that source files contain valid license headers
# https://github.com/googleapis/repo-automation-bots/tree/main/packages/header-checker-lint
# Install: https://github.com/apps/license-header-lint-gcf

allowedCopyrightHolders:
  - 'Google LLC'
allowedLicenses:
  - 'Apache-2.0'
sourceFileExtensions:
  - 'yaml'
  - 'yml'
  - 'sh'
  - 'ts'
  - 'js'
  - 'java'
  - 'html'
  - 'txt'
  - 'kt'
  - 'kts'
  - 'xml'
  - 'gradle'


================================================
FILE: .github/pull_request_template.md
================================================
Thank you for opening a Pull Request!

---

Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [ ] Make sure to open a GitHub issue as a bug/feature request before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> 🦕


================================================
FILE: .github/snippet-bot.yml
================================================
# Copyright 2025 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.



================================================
FILE: .github/stale.yml
================================================
# 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.

# Configuration for probot-stale - https://github.com/probot/stale

# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 120

# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 180

# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []

# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
  - pinned
  - "type: bug"

# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false

# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false

# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false

# Label to use when marking as stale
staleLabel: "stale"

# Comment to post when marking as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  recent activity. Please comment here if it is still valid so that we can
  reprioritize. Thank you!

# Comment to post when removing the stale label.
# unmarkComment: >
#   Your comment here.

# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
  Closing this. Please reopen if you believe it should be addressed. Thank you for your contribution.

# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 10

# Limit to only `issues` or `pulls`
only: issues

# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
#   daysUntilStale: 30
#   markComment: >
#     This pull request has been automatically marked as stale because it has not had
#     recent activity. It will be closed if no further activity occurs. Thank you
#     for your contributions.

# issues:
#   exemptLabels:
#     - confirmed


================================================
FILE: .github/sync-repo-settings.yaml
================================================
# Copyright 2022 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.

# https://github.com/googleapis/repo-automation-bots/tree/main/packages/sync-repo-settings

rebaseMergeAllowed: true
squashMergeAllowed: true
mergeCommitAllowed: false
deleteBranchOnMerge: true
branchProtectionRules:
- pattern: main
  isAdminEnforced: false
  requiresStrictStatusChecks: false
  requiredStatusCheckContexts:
    - 'cla/google'
    - 'test'
    - 'snippet-bot check'
    - 'header-check'
  requiredApprovingReviewCount: 1
  requiresCodeOwnerReviews: true
- pattern: master
  isAdminEnforced: false
  requiresStrictStatusChecks: false
  requiredStatusCheckContexts:
    - 'cla/google'
    - 'test'
    - 'snippet-bot check'
    - 'header-check'
  requiredApprovingReviewCount: 1
  requiresCodeOwnerReviews: true
permissionRules:
  - team: admin
    permission: admin


================================================
FILE: .github/workflows/build.yml
================================================
# 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.

name: Build demos

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the main branch
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  repository_dispatch:
    types: [ build ]
  schedule:
    - cron: '0 0 * * 1'
  workflow_dispatch:

jobs:
  build-all:
    runs-on: ubuntu-22.04

    steps:
      - uses: actions/checkout@v4

      - name: set up JDK 17
        uses: actions/setup-java@v4.2.1
        with:
          java-version: '17'
          distribution: 'adopt'

      - name: Install NDK
        run: |
          sudo ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "ndk;20.0.5594570"

      - name: Create local.defaults.properties
        run: |
          echo "MAPS3D_API_KEY=YOUR_API_KEY" >> local.defaults.properties

      - name: Build and verify all modules
        run: ./gradlew assembleDebug lint --continue

      - name: Run tests
        run: ./gradlew test --continue

      - name: Upload build reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: build-reports
          path: "**/build/reports"


================================================
FILE: .gitignore
================================================
# Build output
build/
.gradle/
.kotlin/

# IDE files
.idea/
*.iml

# Local machine-specific configs
**/local.properties
**/secrets.properties

# OS junk
.DS_Store
.java-version

# This covers new IDEs, like Antigravity
.vscode/
**/bin/

================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Google Open Source Community Guidelines

At Google, we recognize and celebrate the creativity and collaboration of open
source contributors and the diversity of skills, experiences, cultures, and
opinions they bring to the projects and communities they participate in.

Every one of Google's open source projects and communities are inclusive
environments, based on treating all individuals respectfully, regardless of
gender identity and expression, sexual orientation, disabilities,
neurodiversity, physical appearance, body size, ethnicity, nationality, race,
age, religion, or similar personal characteristic.

We value diverse opinions, but we value respectful behavior more.

Respectful behavior includes:

* Being considerate, kind, constructive, and helpful.
* Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or
  physically threatening behavior, speech, and imagery.
* Not engaging in unwanted physical contact.

Some Google open source projects [may adopt][] an explicit project code of
conduct, which may have additional detailed expectations for participants. Most
of those projects will use our [modified Contributor Covenant][].

[may adopt]: https://opensource.google/docs/releasing/preparing/#conduct
[modified Contributor Covenant]: https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/

## Resolve peacefully

We do not believe that all conflict is necessarily bad; healthy debate and
disagreement often yields positive results. However, it is never okay to be
disrespectful.

If you see someone behaving disrespectfully, you are encouraged to address the
behavior directly with those involved. Many issues can be resolved quickly and
easily, and this gives people more control over the outcome of their dispute.
If you are unable to resolve the matter for any reason, or if the behavior is
threatening or harassing, report it. We are dedicated to providing an
environment where participants feel welcome and safe.

## Reporting problems

Some Google open source projects may adopt a project-specific code of conduct.
In those cases, a Google employee will be identified as the Project Steward,
who will receive and handle reports of code of conduct violations. In the event
that a project hasn’t identified a Project Steward, you can report problems by
emailing opensource@google.com.

We will investigate every complaint, but you may not receive a direct response.
We will use our discretion in determining when and how to follow up on reported
incidents, which may range from not taking action to permanent expulsion from
the project and project-sponsored spaces. We will notify the accused of the
report and provide them an opportunity to discuss it before any action is
taken. The identity of the reporter will be omitted from the details of the
report supplied to the accused. In potentially harmful situations, such as
ongoing harassment or threats to anyone's safety, we may take action without
notice.

*This document was adapted from the [IndieWeb Code of Conduct][] and can also
be found at <https://opensource.google/conduct/>.*

[IndieWeb Code of Conduct]: https://indieweb.org/code-of-conduct


================================================
FILE: CONTRIBUTING.md
================================================
# How to become a contributor and submit your own code

## Contributor License Agreements

We'd love to accept your sample apps and patches! Before we can take them, we 
have to jump a couple of legal hurdles.

Please fill out either the individual or corporate Contributor License Agreement
(CLA).

  * If you are an individual writing original source code and you're sure you
    own the intellectual property, then you'll need to sign an [individual CLA]
    (http://code.google.com/legal/individual-cla-v1.0.html).
  * If you work for a company that wants to allow you to contribute your work,
    then you'll need to sign a [corporate CLA]
    (http://code.google.com/legal/corporate-cla-v1.0.html).

Follow either of the two links above to access the appropriate CLA and
instructions for how to sign and return it. Once we receive it, we'll be able to
accept your pull requests.


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2016 Google

   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.


================================================
FILE: PlaceDetailsCompose/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties


================================================
FILE: PlaceDetailsCompose/ARCHITECTURE.md
================================================
# Architecture

This sample application demonstrates a modern **MVVM (Model-View-ViewModel)** architecture integrated with **Jetpack Compose** and **Google Maps Platform**.

## Overview

The app is a single-screen application (`MapScreen`) that allows users to view a map, track their location, and select places to view detailed information using the Google Places UI Kit.

### Key Layers

1.  **UI Layer (Compose)**:
    -   **`MapScreen`**: The main entry point. Handles the Scaffold, Map rendering (via Maps Compose), and UI controls.
    -   **`PlaceDetailsView`**: Bridging components (`PlaceDetailsCompactView`, `PlaceDetailsFullView`) that wrap the View-based Places fragments using `AndroidView`.
    -   **`MapViewModel`**: Holds the UI state (`selectedPlace`, `isFullView`, `deviceLocation`) and handles business logic.

2.  **Data Layer (Repository)**:
    -   **`LocationRepository`**: A light wrapper around `FusedLocationProviderClient`. It exposes location updates as a Kotlin `Flow`, handling permission checks gracefully.

## Key Patterns

### 1. Compose Interoperability (`AndroidView`)
The Google Places UI Kit components (`PlaceDetailsCompactFragment`, `PlaceDetailsFragment`) are standard Android Fragments. To use them in a pure Compose app, we use the `AndroidView` composable to host a `FragmentContainerView`.

-   **Unique IDs**: We generate unique View IDs using `View.generateViewId()` so the `FragmentManager` can correctly identify each container.
-   **Lifecycle Handling**: Fragments are added/removed via transactions inside the `AndroidView` factory.
-   **Updates**: Data updates (like changing the Place ID) are posted to the view queue (`view.post`) to ensure the Fragment is fully attached before loading data.

### 2. State Management with Flows
The `MapViewModel` uses `StateFlow` to expose reactive state to the UI.
-   **`flatMapLatest`**: Used for location updates to automatically switch between "no location" and "location updates" based on permission state.
-   **`combine` / `map`**: derived state is computed reactively.

## Common Integration Challenges

When integrating the Places UI Kit (View-based) into a Jetpack Compose app, there are a few specific implementation details to be aware of:

1.  **"Context must be a FragmentActivity" Crash**:
    -   *Why it happens*: The Places UI Kit fragments (`PlaceDetailsFragment`) rely on the legacy Android `FragmentManager`. This manager is only available in a `FragmentActivity` (or `AppCompatActivity`).
    -   *The Fix*: We assume the Composable is hosted in an Activity that extends `AppCompatActivity` and cast the `Context` to it.

2.  **`view.post { ... }`**:
    -   *Why we do it*: Fragment transactions are asynchronous. If we try to call `fragment.loadWithPlaceId` immediately after adding the fragment, the fragment's view might not be created yet, leading to a crash.
    -   *The Fix*: `view.post` schedules the action to run *after* the current message queue is processed, ensuring the view hierarchy is ready.

3.  **Unique View IDs (`View.generateViewId()`)**:
    -   *Why*: If you have multiple `AndroidView`s hosting fragments (even if one is hidden), they need distinct IDs so the `FragmentManager` doesn't get confused about which container holds which fragment.



================================================
FILE: PlaceDetailsCompose/README.md
================================================
# Place Details Compose Sample

This sample demonstrates how to integrate the **Places UI Kit** (specifically `PlaceDetailsCompactFragment` and `PlaceDetailsFragment`) into a **Jetpack Compose** application.

It showcases how to wrap these View-based fragments using `AndroidView` to create seamless Composable wrappers: `PlaceDetailsCompactView` and `PlaceDetailsFullView`.

## Features

- **Jetpack Compose Integration**: Demonstrates the `AndroidView` pattern for embedding Places UI Kit fragments.
- **Compact & Full Views**: Supports both the Compact (bottom sheet style) and Full (fullscreen style) variants of the UI Kit.
- **Dynamic Toggling**: Users can switch between Compact and Full views at runtime using a toggle switch.
- **Google Maps Integration**: Uses the Maps Compose library to display an interactive map.
- **MVVM Architecture**: Manages state (selected place, view mode) using a `MapViewModel`.
- **Secrets Management**: Securely handles API keys using the Secrets Gradle Plugin.

## Getting Started

1.  **Clone the repository:**
    ```bash
    git clone https://github.com/googlemaps-samples/android-places-demos.git
    ```
2.  **Open in Android Studio:** Open the `PlaceDetailsCompose` directory.
3.  **Add API Key:**
    -   Create `secrets.properties` in the project root.
    -   Add your key: `PLACES_API_KEY="YOUR_API_KEY"` (ensure Places API and Maps SDK are enabled).
4.  **Run:** Build and run on a device/emulator.

## Code Highlights

### 1. Wrapping Fragments in Compose (`PlaceDetailsView.kt`)

The core of this integration is wrapping the `PlaceDetailsCompactFragment` and `PlaceDetailsFragment` in a Composable. We use `AndroidView` to host a `FragmentContainerView`.

**Key Steps:**
-   **Unique ID**: Generate a unique view ID (`View.generateViewId()`) for the container so `FragmentManager` can identify it.
-   **Fragment Management**: In the `update` block, check if the fragment exists. If not, create and add it.
-   **Safe Loading**: Use `view.post { ... }` to call `loadWithPlaceId`. This ensures the fragment's view is fully attached before data loading begins, preventing crashes.

```kotlin
@Composable
fun PlaceDetailsCompactView(place: PointOfInterest, ...) {
    val fragmentContainerId = remember { View.generateViewId() }
    
    AndroidView(
        factory = { context ->
            FragmentContainerView(context).apply { id = fragmentContainerId }
        },
        update = { view ->
            // ... Fragment transaction logic ...
            view.post { fragment.loadWithPlaceId(place.placeId) }
        }
    )
}
```

### 2. Switching Views (`MapScreen.kt`)

The app demonstrates how to dynamically switch between the Compact and Full views while maintaining the selected place context.

```kotlin
var isFullView by remember { mutableStateOf(false) }

if (isFullView) {
    PlaceDetailsFullView(place = place, ...)
} else {
    PlaceDetailsCompactView(place = place, ...)
}
```

### 3. Handling Events

We use `PlaceLoadListener` attached to the fragment to listen for success/failure events. These are propagated back to the Compose layer via callbacks (e.g., `onDismiss`).

## License

```
Copyright 2025 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.
```


================================================
FILE: PlaceDetailsCompose/build.gradle.kts
================================================
/*
 * Copyright 2025 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.
 */

// The `plugins` block is where we apply Gradle plugins to this module.
// Plugins add new tasks and configurations to our build process.
plugins {
    id("places-demo.android.application")
    // This plugin enables Kotlin support in the Android project, allowing us to write code in Kotlin.
    alias(libs.plugins.kotlin.android)
    // This plugin from Google helps manage API keys and other secrets by reading them from a `secrets.properties`
    // file (which should be in .gitignore) and exposing them in the `BuildConfig` file at compile time.
    // This is crucial for keeping sensitive data out of version control.
    id("places-demo.secrets")
    // This plugin provides the necessary integration for using Jetpack Compose with the Kotlin compiler.
    alias(libs.plugins.kotlin.compose)
}

// The `android` block is where we configure all the Android-specific build options.
android {
    // The `namespace` is a unique identifier for the app's generated R class. It's also used
    // as the default `applicationId` if not specified in `defaultConfig`.
    namespace = "com.example.placedetailscompose"

    defaultConfig {
        // `applicationId` is the unique identifier for the app on the Google Play Store and on the device.
        applicationId = "com.example.placedetailscompose"
        // `minSdk` is the minimum API level required to run the app. Devices below this level cannot install it.
        minSdk = 27
        versionCode = 1
        versionName = "1.0"

        // Specifies the instrumentation runner for running Android tests.
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        // The `release` block configures settings for the release build of the app.
        release {
            // `isMinifyEnabled` enables code shrinking with R8 to reduce the app's size.
            // It's disabled here for simplicity in a sample app, but highly recommended for production.
            isMinifyEnabled = false
            // `proguardFiles` specifies the files that define the R8 shrinking and obfuscation rules.
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    kotlin {
        // Configures Kotlin-specific compiler options.
        compilerOptions {
            freeCompilerArgs.addAll(
                "-opt-in=kotlin.RequiresOptIn",
                "-Xannotation-default-target=param-property"
            )
        }
    }
    buildFeatures {
        // `compose` enables Jetpack Compose for the project.
        compose = true
        // `buildConfig` generates a `BuildConfig` class that contains constants from the build configuration,
        // such as the API key from the secrets plugin.
        buildConfig = true
        // `viewBinding` generates a binding class for each XML layout file.
        viewBinding = true
    }
    composeOptions {
        // Sets the version of the Kotlin compiler extension for Compose. This version must be
        // compatible with the Kotlin version used in the project.
        kotlinCompilerExtensionVersion = "1.5.1"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

// The `dependencies` block is where we declare all the external libraries the app needs.
// These are fetched from repositories like Maven Central and Google's Maven repository.
dependencies {
    // --- Core AndroidX & UI Libraries ---
    // These are foundational libraries for building modern Android apps.
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(libs.androidx.lifecycle.viewmodel.compose)
    implementation(libs.androidx.fragment.ktx)
    implementation(libs.androidx.compose.material.icons.core)

    // --- Google Play Services ---
    // These are the essential libraries for this sample, providing Maps and Places functionality.
    implementation(libs.google.maps.services) // The core SDK for embedding Google Maps.
    implementation(libs.places) // The SDK for the Places UI Kit (PlaceDetails fragments).
    implementation(libs.play.services.location) // Needed for the FusedLocationProviderClient to get the device's location.
    implementation(libs.maps.compose)
    implementation(libs.maps.compose.widgets)
    implementation(libs.maps.utils.ktx)
    implementation(libs.material) // For Material Design components (used in XML layouts).

    // --- Jetpack Compose ---
    // These libraries are for building UIs with Jetpack Compose.
    implementation(libs.androidx.material3) // The latest Material Design components for Compose.
    implementation(platform(libs.androidx.compose.bom)) // The Compose Bill of Materials (BOM) ensures all Compose libraries use compatible versions.
    implementation(libs.androidx.ui.tooling.preview) // For displaying @Preview composables in Android Studio.
    implementation(libs.androidx.ui.viewbinding)
    implementation(libs.androidx.material.icons.extended)
    debugImplementation(libs.androidx.ui.tooling) // Provides tools for inspecting Compose UIs.

    // --- Testing Libraries ---
    // These libraries are for writing and running tests.
    // `testImplementation` is for local unit tests (running on the JVM).
    testImplementation(libs.junit)
    // `androidTestImplementation` is for instrumented tests (running on an Android device or emulator).
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core) // For UI testing with the View system.
    // AndroidX libraries for creating test rules and running tests.
    androidTestImplementation(libs.androidx.test.rules)
    androidTestImplementation(libs.androidx.test.runner)

    // --- Compose Testing ---
    // These are specific to testing Jetpack Compose UIs.
    androidTestImplementation(platform(libs.androidx.compose.bom)) // BOM for testing libraries.
    androidTestImplementation(libs.androidx.ui.test.junit4) // The main library for Compose UI tests.
    debugImplementation(libs.androidx.ui.test.manifest) // Provides a manifest for UI tests.
}


================================================
FILE: PlaceDetailsCompose/local.defaults.properties
================================================
PLACES_API_KEY=YOUR_API_KEY
MAPS_API_KEY=YOUR_API_KEY
MAP_ID=YOUR_MAP_ID


================================================
FILE: PlaceDetailsCompose/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:name=".PlaceDetailsComposeApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.PlaceDetailsCompose"
        tools:targetApi="36">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}"/>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/MainActivity.kt
================================================
// Copyright 2025 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.placedetailscompose

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.example.placedetailscompose.ui.map.MapScreen
import com.example.placedetailscompose.ui.theme.PlaceDetailsComposeTheme
import com.google.android.libraries.places.api.Places

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

        // Retrieve the API key from the local.properties file.
        // See https://github.com/googlemaps/android-places-demos#installation for more details.
        val apiKey = BuildConfig.PLACES_API_KEY
        if (apiKey.isEmpty() || apiKey == "YOUR_API_KEY") {
            Log.e("PlacesCompose", "No api key")
            Toast.makeText(
                this,
                "Add your own API_KEY in local.properties",
                Toast.LENGTH_LONG
            ).show()
            finish()
            return
        }

        // Initialize the Places SDK. This must be done before calling any other Places API methods.
        // The 'newPlacesApiEnabled' flag indicates that the new Places API should be used.
        // This can happen in an Activity or the Application.
        Places.initializeWithNewPlacesApiEnabled(applicationContext, apiKey)

        enableEdgeToEdge()
        setContent {
            val window = this.window
            val insetsController = WindowCompat.getInsetsController(window, window.decorView)

            // Educational Note: We are handling permissions directly within the Compose scope
            // here to keep this Place Details sample self-contained and easy to follow.
            // In a production app, you might prefer to hoist this logic to a ViewModel
            // or a dedicated permission handler class.

            // Check if we already have the permission.
            // Using ContextCompat.checkSelfPermission ensures we respect the state if the 
            // user granted it previously via system settings.
            var hasPermission by remember {
                mutableStateOf(
                    ContextCompat.checkSelfPermission(
                        this,
                        Manifest.permission.ACCESS_FINE_LOCATION
                    ) == PackageManager.PERMISSION_GRANTED
                )
            }

            // The standard, modern Compose way to register for Activity Results (like Permissions)
            // within a Composable scope.
            val launcher = rememberLauncherForActivityResult(
                contract = ActivityResultContracts.RequestPermission(),
                onResult = { granted ->
                    if (granted) {
                        hasPermission = true
                    } else {
                        Toast.makeText(this, "Location permission is required to use this app.", Toast.LENGTH_LONG).show()
                    }
                }
            )

            // Trigger the permission request when this Composable first enters the composition.
            // The 'Unit' key ensures this side-effect only runs once on mount.
            LaunchedEffect(Unit) {
                if (!hasPermission) {
                    launcher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
                }
            }

            SideEffect {
                insetsController.hide(WindowInsetsCompat.Type.systemBars())
                insetsController.systemBarsBehavior =
                    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            }

            PlaceDetailsComposeTheme {
                if (hasPermission) {
                    MapScreen()
                } else {
                    Box(
                        modifier = Modifier.fillMaxSize(),
                        contentAlignment = Alignment.Center
                    ) {
                        Button(onClick = { launcher.launch(Manifest.permission.ACCESS_FINE_LOCATION) }) {
                            Text("Grant Location Permission")
                        }
                    }
                }
            }
        }
    }
}


================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/PlaceDetailsComposeApplication.kt
================================================
/*
* Copyright 2025 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.placedetailscompose

import android.app.Application
import android.content.pm.PackageManager
import android.util.Log
import android.widget.Toast
import java.util.Objects
import kotlin.text.isBlank

/**
 * `PlaceDetailsComposeApplication` is a custom Application class.
 *
 * This class is responsible for application-wide initialization and setup,
 * such as checking for the presence and validity of the API key during the
 * application's startup.
 *
 * It extends the [Application] class and overrides the [.onCreate]
 * method to perform these initialization tasks.
 */
class PlaceDetailsComposeApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        checkApiKey()
    }

    /**
     * Checks if the API key for Google Maps is properly configured in the application's metadata.
     *
     * This method retrieves the API key from the application's metadata, specifically looking for
     * a string value associated with the key "com.google.android.geo.API_KEY".
     * The key must be present, not blank, and not set to the placeholder value "DEFAULT_API_KEY".
     *
     * If any of these checks fail, a Toast message is displayed indicating that the API key is missing or
     * incorrectly configured, and a RuntimeException is thrown.
     */
    private fun checkApiKey() {
        try {
            val appInfo =
                packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
            val bundle = Objects.requireNonNull(appInfo.metaData)

            val apiKey =
                bundle.getString("com.google.android.geo.API_KEY") // Key name is important!

            if (apiKey == null || apiKey.isBlank() || apiKey == "DEFAULT_API_KEY") {
                Toast.makeText(
                    this,
                    getString(R.string.error_api_key_missing),
                    Toast.LENGTH_LONG
                ).show()
                throw RuntimeException(getString(R.string.error_api_key_missing))
            }
        } catch (e: PackageManager.NameNotFoundException) {
            Log.e(TAG, "Package name not found.", e)
            throw RuntimeException("Error getting package info.", e)
        } catch (e: NullPointerException) {
            Log.e(TAG, "Error accessing meta-data.", e) // Handle the case where meta-data is completely missing.
            throw RuntimeException("Error accessing meta-data in manifest", e)
        }
    }

    /**
     * Retrieves the map ID from the BuildConfig or string resource.
     *
     * @return The valid map ID or null if no valid map ID is found.
     */
    val mapId: String? by lazy {
        if (BuildConfig.MAP_ID != "YOUR_MAP_ID") {
            BuildConfig.MAP_ID
        } else if (getString(R.string.map_id) != "YOUR_MAP_ID") {
            getString(R.string.map_id)
        } else {
            Log.w(TAG, "Map ID is not set. See README for instructions.")
            Toast.makeText(this, getString(R.string.error_map_id_missing), Toast.LENGTH_LONG)
                .show()
            null
        }
    }

    companion object {
        private const val TAG = "ApiDemoApplication"
    }
}

================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/repository/LocationRepository.kt
================================================
// Copyright 2025 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.placedetailscompose.repository

import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
import android.os.Looper
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow

class LocationRepository(context: Context) {

    private val fusedLocationClient: FusedLocationProviderClient =
        LocationServices.getFusedLocationProviderClient(context)

    @SuppressLint("MissingPermission")
    fun getDeviceLocation(): Flow<Location> = callbackFlow {
        val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000L)
            .setWaitForAccurateLocation(false)
            .setMinUpdateIntervalMillis(5000L)
            .build()

        val locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                locationResult.lastLocation?.let { trySend(it) }
            }
        }

        try {
            fusedLocationClient.requestLocationUpdates(
                locationRequest,
                locationCallback,
                Looper.getMainLooper()
            )
        } catch (e: SecurityException) {
            // Permissions were likely denied.
            // In a real app, we might want to emit an error state or log this.
            // For now, we just close the flow to avoid a crash.
            close(e)
        }

        awaitClose {
            fusedLocationClient.removeLocationUpdates(locationCallback)
        }
    }
}


================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/map/MapScreen.kt
================================================
// Copyright 2025 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.placedetailscompose.ui.map

import android.Manifest
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.ui.draw.scale
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.placedetailscompose.R
import com.example.placedetailscompose.viewmodels.MapViewModel
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.maps.android.compose.Circle
import com.google.maps.android.compose.ComposeMapColorScheme
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.MapProperties
import com.google.maps.android.compose.MapType
import com.google.maps.android.compose.MapUiSettings
import com.google.maps.android.compose.rememberCameraPositionState
import com.google.maps.android.ktx.utils.sphericalDistance
import com.google.maps.android.ktx.utils.withSphericalOffset
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/**
 * The main screen of the app. This screen shows a map and allows the user to select a
 * point of interest to see details about it.
 */
@Composable
fun MapScreen(
    viewModel: MapViewModel = viewModel()
) {
    val context = LocalContext.current
    val deviceLocation by viewModel.deviceLocation.collectAsState()
    val selectedPlace by viewModel.selectedPlace.collectAsState()
    val isMapFollowingUser by viewModel.isMapFollowingUser.collectAsState()
    val hasAnimatedToPlace by viewModel.hasAnimatedToPlace.collectAsState()
    
    // **View Mode State**
    var isFullView by rememberSaveable { mutableStateOf(false) }

    // **Coordinate Mode State**
    val isCoordinateMode by viewModel.isCoordinateMode.collectAsState()

    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(viewModel.sydney, 13f)
    }

    val permissionDeniedString = stringResource(R.string.location_permission_denied)
    val locationPermissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true ||
            permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true
        ) {
            viewModel.onPermissionGranted()
        } else {
            Toast.makeText(context, permissionDeniedString, Toast.LENGTH_SHORT).show()
        }
    }

    LaunchedEffect(Unit) {
        if (ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            viewModel.onPermissionGranted()
        } else {
            locationPermissionLauncher.launch(
                arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                )
            )
        }
    }

    LaunchedEffect(selectedPlace) {
        selectedPlace?.let { place ->
            val latLng = place.location
            if (latLng != null) {
                val focalPoint = latLng.withSphericalOffset(300.0, 180.0)
                val placeCameraPosition = CameraPosition.builder()
                    .target(focalPoint)
                    .zoom(15f)
                    .build()
                if (hasAnimatedToPlace) {
                    cameraPositionState.move(CameraUpdateFactory.newCameraPosition(placeCameraPosition))
                } else {
                    cameraPositionState.animate(CameraUpdateFactory.newCameraPosition(placeCameraPosition), 1000)
                    viewModel.onAnimateToPlaceFinish()
                }
            }
        }
    }

    LaunchedEffect(deviceLocation, isMapFollowingUser) {
        deviceLocation?.let { location ->
            if (isMapFollowingUser) {
                val currentPosition = cameraPositionState.position.target
                val distance = currentPosition.sphericalDistance(location)
                if (distance > 100) {
                    cameraPositionState.animate(CameraUpdateFactory.newLatLngZoom(location, 15f))
                }
            }
        }
    }

    val selectedCompactContent by viewModel.selectedCompactContent.collectAsState()
    val selectedFullContent by viewModel.selectedFullContent.collectAsState()
    var showContentSelectionDialog by rememberSaveable { mutableStateOf(false) }
    val coroutineScope = rememberCoroutineScope()

    var showSettingsButton by remember { mutableStateOf(true) }
    LaunchedEffect(showSettingsButton) {
        if (showSettingsButton) {
            delay(5000)
            showSettingsButton = false
        }
    }

    if (cameraPositionState.isMoving) {
        // Reset the timer whenever the user is dragging the map.
        showSettingsButton = true
        viewModel.onMapDragged()
    }

    Box(modifier = Modifier.fillMaxSize()) {
        GoogleMap(
            modifier = Modifier.fillMaxSize(),
            cameraPositionState = cameraPositionState,
            properties = MapProperties(
                isMyLocationEnabled = true,
                mapType = MapType.NORMAL
            ),
            uiSettings = MapUiSettings(
                myLocationButtonEnabled = true,
                zoomControlsEnabled = false
            ),
            onMapLoaded = {
                showSettingsButton = true
            },
            onPOIClick = { poi ->
                showSettingsButton = true
                if (!isCoordinateMode) {
                    coroutineScope.launch {
                        val cameraPosition = CameraPosition.builder()
                            .target(poi.latLng)
                            .zoom(15f)
                            .build()
                        cameraPositionState.animate(CameraUpdateFactory.newCameraPosition(cameraPosition), 2000)
                    }
                    viewModel.onPoiClicked(poi)
                }
            },
            onMapClick = { latLng ->
                showSettingsButton = true
                viewModel.onMapClicked(latLng)
            },
            mapColorScheme = ComposeMapColorScheme.FOLLOW_SYSTEM
        ) {
            selectedPlace?.location?.let {
                Circle(
                    center = it,
                    radius = 75.0,
                    fillColor = Color(0x880088FF),
                    strokeWidth = 2f,
                    strokeColor = Color(0xAA000000)
                )
            }
        }

        var isControlsExpanded by rememberSaveable { mutableStateOf(false) }

        AnimatedVisibility(
            visible = showSettingsButton,
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            Column(
                modifier = Modifier
                    .padding(top = 48.dp, start = 16.dp)
                    .align(Alignment.TopStart),
                horizontalAlignment = Alignment.Start
            ) {
                FloatingActionButton(
                    onClick = {
                        isControlsExpanded = !isControlsExpanded
                        // Keep the button visible while the controls are expanded
                        showSettingsButton = true
                    },
                    modifier = Modifier.padding(bottom = 8.dp),
                    containerColor = MaterialTheme.colorScheme.surface,
                    contentColor = MaterialTheme.colorScheme.onSurface
                ) {
                    Icon(
                        imageVector = if (isControlsExpanded) Icons.Default.ChevronLeft else Icons.Default.Settings,
                        contentDescription = if (isControlsExpanded) stringResource(R.string.collapse_settings) else stringResource(R.string.expand_settings)
                    )
                }

                AnimatedVisibility(
                    visible = isControlsExpanded,
                    enter = expandHorizontally(expandFrom = Alignment.Start) + fadeIn(),
                    exit = shrinkHorizontally(shrinkTowards = Alignment.Start) + fadeOut()
                ) {
                    androidx.compose.material3.ElevatedCard(
                        modifier = Modifier
                            .padding(8.dp)
                            .background(MaterialTheme.colorScheme.surface, RoundedCornerShape(12.dp))
                    ) {
                        Column(
                            modifier = Modifier.padding(16.dp),
                            horizontalAlignment = Alignment.CenterHorizontally
                        ) {
                            Row(
                                modifier = Modifier.padding(bottom = 8.dp),
                                horizontalArrangement = Arrangement.spacedBy(24.dp),
                                verticalAlignment = Alignment.CenterVertically
                            ) {
                                Column(
                                    horizontalAlignment = Alignment.CenterHorizontally,
                                    modifier = Modifier.padding(end = 16.dp)
                                ) {
                                    Text(
                                        text = if (isFullView) stringResource(R.string.full_view) else stringResource(R.string.compact_view),
                                        style = MaterialTheme.typography.labelSmall,
                                        modifier = Modifier.padding(bottom = 4.dp)
                                    )
                                    Switch(
                                        checked = isFullView,
                                        onCheckedChange = { isFullView = it },
                                        modifier = Modifier.scale(0.8f)
                                    )
                                }
                                Column(
                                    horizontalAlignment = Alignment.CenterHorizontally
                                ) {
                                    Text(
                                        text = if (isCoordinateMode) stringResource(R.string.coords_mode) else stringResource(R.string.poi_mode),
                                        style = MaterialTheme.typography.labelSmall,
                                        modifier = Modifier.padding(bottom = 4.dp)
                                    )
                                    Switch(
                                        checked = isCoordinateMode,
                                        onCheckedChange = { viewModel.onToggleCoordinateMode(it) },
                                        modifier = Modifier.scale(0.8f)
                                    )
                                }
                            }
                            androidx.compose.material3.HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp))
                            androidx.compose.material3.FilledTonalButton(
                                onClick = { showContentSelectionDialog = true },
                                modifier = Modifier.fillMaxWidth()
                            ) {
                                Text(stringResource(R.string.select_fields))
                            }
                        }
                    }
                }
            }
        }

        selectedPlace?.let { place ->
            if (isFullView) {
                PlaceDetailsFullView(
                    place = place,
                    onDismiss = { viewModel.onDismissPlace() },
                    content = selectedFullContent,
                    modifier = Modifier
                        .align(Alignment.BottomCenter)
                        .fillMaxWidth()
                        .fillMaxHeight(0.9f)
                )
            } else {
                PlaceDetailsCompactView(
                    place = place,
                    onDismiss = { viewModel.onDismissPlace() },
                    content = selectedCompactContent,
                    modifier = Modifier
                        .align(Alignment.BottomCenter)
                        .fillMaxWidth()
                )
            }
        }

        if (showContentSelectionDialog) {
            if (isFullView) {
                PlaceContentSelectionDialog(
                    title = stringResource(R.string.select_full_view_fields),
                    allContent = com.google.android.libraries.places.widget.PlaceDetailsFragment.Content.values().toList(),
                    selectedContent = selectedFullContent,
                    onSelectionChanged = { viewModel.updateFullContent(it) },
                    onDismissRequest = { showContentSelectionDialog = false },
                    nameProvider = { it.name }
                )
            } else {
                PlaceContentSelectionDialog(
                    title = stringResource(R.string.select_compact_view_fields),
                    allContent = com.google.android.libraries.places.widget.PlaceDetailsCompactFragment.Content.values().toList(),
                    selectedContent = selectedCompactContent,
                    onSelectionChanged = { viewModel.updateCompactContent(it) },
                    onDismissRequest = { showContentSelectionDialog = false },
                    nameProvider = { it.name }
                )
            }
        }
    }
}

================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/map/PlaceContentSelectionDialog.kt
================================================
// Copyright 2025 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.placedetailscompose.ui.map

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.res.stringResource

@Composable
fun <T> PlaceContentSelectionDialog(
    title: String,
    allContent: List<T>,
    selectedContent: List<T>,
    onSelectionChanged: (List<T>) -> Unit,
    onDismissRequest: () -> Unit,
    nameProvider: (T) -> String
) {
    AlertDialog(
        onDismissRequest = onDismissRequest,
        title = { Text(text = title) },
        text = {
            LazyColumn {
                items(allContent) { item ->
                    val isSelected = selectedContent.contains(item)
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .clickable {
                                val newSelection = if (isSelected) {
                                    selectedContent - item
                                } else {
                                    selectedContent + item
                                }
                                onSelectionChanged(newSelection)
                            }
                            .padding(vertical = 8.dp),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        Checkbox(
                            checked = isSelected,
                            onCheckedChange = null // Handled by Row click
                        )
                        Text(
                            text = nameProvider(item),
                            modifier = Modifier.padding(start = 8.dp),
                            style = MaterialTheme.typography.bodyMedium
                        )
                    }
                }
            }
        },
        confirmButton = {
            TextButton(onClick = onDismissRequest) {
                Text(stringResource(com.example.placedetailscompose.R.string.done))
            }
        }
    )
}


================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/map/PlaceDetailsView.kt
================================================
// Copyright 2025 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.placedetailscompose.ui.map

import android.content.res.Configuration
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.FragmentContainerView
import androidx.compose.ui.res.stringResource
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.PlaceDetailsCompactFragment
import com.google.android.libraries.places.widget.PlaceLoadListener
import com.google.android.libraries.places.widget.PlaceDetailsFragment
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.background
import androidx.compose.foundation.shape.CircleShape
import com.google.android.libraries.places.widget.model.Orientation

/**
 * This composable displays the **Compact** version of the Place Details UI.
 *
 * **Why use `AndroidView`?**
 * The Places UI Kit components (`PlaceDetailsCompactFragment` and `PlaceDetailsFragment`) are currently
 * implemented as Android Fragments, not native Composables. To use them in a Jetpack Compose app,
 * we need to bridge the gap using `AndroidView`. This allows us to host a legacy View (in this case,
 * a `FragmentContainerView`) inside our Compose layout.
 *
 * @param place The point of interest to display details for.
 * @param onDismiss A callback to be invoked when the place details fragment is dismissed.
 */
@Composable
fun PlaceDetailsCompactView(
    place: Place,
    onDismiss: () -> Unit,
    modifier: Modifier = Modifier,
    content: List<PlaceDetailsCompactFragment.Content> = PlaceDetailsCompactFragment.ALL_CONTENT,
) {
    // We need to know the device orientation to tell the Fragment how to lay itself out.
    // Although Compose handles layout differently, the underlying Fragment still relies on this signal.
    val orientation =
        if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Orientation.HORIZONTAL
        } else {
            Orientation.VERTICAL
        }

    val context = LocalContext.current

    // **Why generate a View ID?**
    // The FragmentManager needs a unique ID to identify the container where the fragment will be placed.
    // `View.generateViewId()` gives us a safe, unique integer that won't collide with other views.
    // We use `remember` so this ID persists across recompositions.
    val fragmentContainerId = remember { View.generateViewId() }

    // We need the FragmentManager to perform Fragment transactions (adding/removing the fragment).
    // We cast the Context to AppCompatActivity assuming this Composable is hosted within one.
    // In a production app, you might want a more robust way to provide the FragmentManager.
    val fragmentManager = remember(context) {
        (context as? AppCompatActivity)?.supportFragmentManager
            ?: throw IllegalStateException("Context must be a FragmentActivity")
    }

    val fragment = remember(fragmentManager, fragmentContainerId, orientation, content) {
        fragmentManager.findFragmentById(fragmentContainerId) as? PlaceDetailsCompactFragment
            ?: PlaceDetailsCompactFragment.newInstance(
                content,
                orientation,
            ).also { fragment ->
                // **Listening for Load Events**
                fragment.setPlaceLoadListener(object : PlaceLoadListener {
                    override fun onSuccess(place: Place) {
                        Log.d("PlaceDetails", "Loading details for: ${place.id} at ${place.location}")
                    }

                    override fun onFailure(e: Exception) {
                        Log.d("PlaceDetailsView", "Place failed to load place: ${e.message}")
                        onDismiss()
                    }
                })
            }
    }

    Box(modifier = modifier.fillMaxWidth()) {
        AndroidView(
            modifier = Modifier.fillMaxWidth(),
            factory = { context ->
                // **The Factory Block**
                // This runs only once when the AndroidView is first created.
                // We create the container view that will hold our Fragment.
                FragmentContainerView(context).apply {
                    id = fragmentContainerId
                    // Ensure the fragment is added.
                    // We use commit() (async) to allow the view to be attached before the transaction runs.
                    if (fragmentManager.findFragmentById(fragmentContainerId) == null) {
                        fragmentManager.beginTransaction()
                            .add(fragmentContainerId, fragment)
                            .commit()
                    }
                }
            },
            update = { view ->
                // **The Update Block**
                // This runs whenever the Composable recomposes (e.g., when `place` changes).

                // We post the update to ensure it runs after the fragment transaction has completed
                // and the fragment's view hierarchy is fully initialized.
                view.post {
                    // Load the place data
                    if (place.id != null) {
                        fragment.loadWithPlaceId(place.id!!)
                    } else if (place.location != null) {
                         fragment.loadWithCoordinates(place.location!!)
                    } else {
                        Log.e("PlaceDetailsView", "Place has no ID and no location: $place")
                    }
                }
            }
        )

        // Close Button
        IconButton(
            onClick = onDismiss,
            modifier = Modifier
                .align(Alignment.TopEnd)
                .padding(16.dp)
                .background(MaterialTheme.colorScheme.surface.copy(alpha = 0.7f), shape = CircleShape)
        ) {
            Icon(
                imageVector = Icons.Default.Close,
                contentDescription = stringResource(com.example.placedetailscompose.R.string.close),
                tint = MaterialTheme.colorScheme.onSurface
            )
        }
    }
}

/**
 * This composable displays the **Full** version of the Place Details UI.
 *
 * It follows the same pattern as [PlaceDetailsCompactView], but wraps the [PlaceDetailsFragment]
 * instead. This fragment takes up more screen space and shows more detailed information.
 */
@Composable
fun PlaceDetailsFullView(
    place: Place,
    onDismiss: () -> Unit,
    modifier: Modifier = Modifier,
    content: List<PlaceDetailsFragment.Content> = PlaceDetailsFragment.STANDARD_CONTENT,
) {
    val orientation =
        if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Orientation.HORIZONTAL
        } else {
            Orientation.VERTICAL
        }

    val context = LocalContext.current
    val fragmentManager = remember(context) {
        (context as? AppCompatActivity)?.supportFragmentManager
            ?: throw IllegalStateException("Context must be a FragmentActivity")
    }

    val fragmentContainerId = remember { View.generateViewId() }

    val fragment = remember(fragmentManager, fragmentContainerId, orientation, content) {
        fragmentManager.findFragmentById(fragmentContainerId) as? PlaceDetailsFragment
            ?: PlaceDetailsFragment.newInstance(
                content,
                orientation,
            ).also { fragment ->
                fragment.setPlaceLoadListener(object : PlaceLoadListener {
                    override fun onSuccess(place: Place) {
                        Log.d("PlaceDetailsFullView", "Place loaded: $place")
                    }

                    override fun onFailure(e: Exception) {
                        Log.d("PlaceDetailsFullView", "Place failed to load place: ${e.message}")
                        onDismiss()
                    }
                })
            }
    }

    Box(modifier = modifier.fillMaxSize()) {
        // Container for the bottom sheet content
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .align(Alignment.BottomCenter)
        ) {
            AndroidView(
                modifier = Modifier.fillMaxWidth(),
                factory = { context ->
                    FragmentContainerView(context).apply {
                        id = fragmentContainerId
                        if (fragmentManager.findFragmentById(fragmentContainerId) == null) {
                            fragmentManager.beginTransaction()
                                .add(fragmentContainerId, fragment)
                                .commit()
                        }
                    }
                },
                update = { view ->
                    view.post {
                        if (place.id != null) {
                            fragment.loadWithPlaceId(place.id!!)
                        } else if (place.location != null) {
                            fragment.loadWithCoordinates(place.location!!)
                        } else {
                             Log.e("PlaceDetailsFullView", "Place has no ID and no location: $place")
                        }
                    }
                }
            )

            // Close Button
            IconButton(
                onClick = onDismiss,
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(16.dp)
                    .background(MaterialTheme.colorScheme.surface.copy(alpha = 0.7f), shape = CircleShape)
            ) {
                Icon(
                    imageVector = Icons.Default.Close,
                    contentDescription = stringResource(com.example.placedetailscompose.R.string.close),
                    tint = MaterialTheme.colorScheme.onSurface
                )
            }
        }
    }
}



================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Color.kt
================================================
// Copyright 2025 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.placedetailscompose.ui.theme

import androidx.compose.ui.graphics.Color

val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)

val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)


================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Theme.kt
================================================
// Copyright 2025 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.placedetailscompose.ui.theme

import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

private val DarkColorScheme = darkColorScheme(
    primary = Purple80,
    secondary = PurpleGrey80,
    tertiary = Pink80
)

private val LightColorScheme = lightColorScheme(
    primary = Purple40,
    secondary = PurpleGrey40,
    tertiary = Pink40
)

@Composable
fun PlaceDetailsComposeTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val activity = view.context as Activity
            activity.window.statusBarColor = android.graphics.Color.TRANSPARENT
            WindowCompat.getInsetsController(activity.window, view).isAppearanceLightStatusBars = !darkTheme
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content
    )
}


================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Typography.kt
================================================
// Copyright 2025 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.placedetailscompose.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

// Set of Material typography styles to start with
val Typography = Typography(
    bodyLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.5.sp
    )
)


================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Utils.kt
================================================
/*
 * Copyright 2025 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
 *
 *     https://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.placedetailscompose.ui.theme

import android.app.Activity
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat

/**
 * Sets the status bar color for the given Activity, handling different API levels and modern
 * best practices to avoid the deprecation warning.
 *
 * @param activity The target Activity.
 * @param color The color to set (e.g., Color.Red.toArgb()).
 * @param isLight True if the status bar content (icons/text) should be dark (for light backgrounds).
 */
fun setStatusBarColor(activity: Activity, color: Int, isLight: Boolean) {
    // Set the color directly on the Window
    // This property is used across many API levels, and while the deprecated method
    // is often the one that causes the linter warning, accessing the property directly
    // is the way to set it in a modern way for pre-API 35 devices.
    @Suppress("DEPRECATION")
    activity.window.statusBarColor = color

    // --- Modern System Insets & Light Status Bar Handling ---

    // 1. Get the WindowInsetsControllerCompat (backward-compatible controller)
    val controller = WindowCompat.getInsetsController(activity.window, activity.findViewById<View>(android.R.id.content))

    // 2. Set the appearance (dark icons/text for light status bar background)
    controller.isAppearanceLightStatusBars = isLight

    // Optional: Ensure the window content is drawn *behind* the status bar
    // This is the core of modern edge-to-edge handling and what the deprecation message suggests.
    WindowCompat.setDecorFitsSystemWindows(activity.window, false)
}

================================================
FILE: PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/viewmodels/MapViewModel.kt
================================================
// Copyright 2025 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.placedetailscompose.viewmodels

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.example.placedetailscompose.repository.LocationRepository
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.PointOfInterest
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

private const val TAG = "MapViewModel"

class MapViewModel(application: Application) : AndroidViewModel(application) {
    private val locationRepository = LocationRepository(application)

    val sydney = LatLng(40.01833081193422, -105.27805050328878)

    // **Permission Handling**
    // We use a StateFlow to track whether location permissions have been granted.
    // This is crucial because we don't want to start collecting location updates
    // until we know we have the necessary permissions.
    private val _permissionGranted = MutableStateFlow(false)

    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
    val deviceLocation: StateFlow<LatLng?> = _permissionGranted
        .flatMapLatest { hasPermission ->
            // **Lazy Location Collection**
            // We use `flatMapLatest` to switch between flows based on the permission state.
            // If permission is granted, we start collecting from the repository.
            // If not, we emit `null` (or keep the previous state).
            // This prevents `SecurityException` crashes and ensures we only ask for location
            // when it's safe to do so.
            if (hasPermission) {
                locationRepository.getDeviceLocation()
            } else {
                flowOf(null)
            }
        }
        .map { it?.let { loc -> LatLng(loc.latitude, loc.longitude) } }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)

    /**
     * Called when the UI has confirmed that location permissions are granted.
     * This triggers the [deviceLocation] flow to start fetching updates.
     */
    fun onPermissionGranted() {
        _permissionGranted.value = true
    }

    private val _selectedPlace = MutableStateFlow<com.google.android.libraries.places.api.model.Place?>(null)
    val selectedPlace = _selectedPlace.asStateFlow()

    // **User Tracking State**
    // This state determines if the map camera should automatically follow the user's device location.
    // It starts as `true` (following) but can be disabled by user interaction (dragging).
    private val _isMapFollowingUser = MutableStateFlow(true)
    val isMapFollowingUser: StateFlow<Boolean> = _isMapFollowingUser.asStateFlow()

    // **Coordinate Mode State**
    // This state determines whether clicking the map triggers Place Details for the clicked coordinates.
    private val _isCoordinateMode = MutableStateFlow(false)
    val isCoordinateMode: StateFlow<Boolean> = _isCoordinateMode.asStateFlow()

    private val _hasAnimatedToPlace = MutableStateFlow(false)
    val hasAnimatedToPlace: StateFlow<Boolean> = _hasAnimatedToPlace.asStateFlow()

    fun onAnimateToPlaceFinish() {
        _hasAnimatedToPlace.value = true
    }

    /**
     * Called when the user manually drags the map.
     * We disable user tracking so the map doesn't jump back to the user's location while they are exploring.
     */
    fun onMapDragged() {
        _isMapFollowingUser.value = false
    }

    /**
     * Called when the "My Location" button is clicked.
     * We re-enable user tracking to snap the camera back to the user's location.
     */
    fun onMyLocationClicked() {
        _isMapFollowingUser.value = true
    }

    fun onPoiClicked(poi: PointOfInterest) {
        // When a POI is clicked, we create a Place object with the ID and LatLng.
        // This allows us to load details using the Place ID.
        val place = com.google.android.libraries.places.api.model.Place.builder()
            .setId(poi.placeId)
            .setLocation(poi.latLng)
            .setDisplayName(poi.name)
            .build()
        _selectedPlace.value = place
    }

    fun onMapClicked(latLng: LatLng) {
        if (_isCoordinateMode.value) {
            // In Coordinate Mode, we create a Place object with just the LatLng.
            // The Place Details UI will load details for this location.
            val place = com.google.android.libraries.places.api.model.Place.builder()
                .setLocation(latLng)
                .build()
            _selectedPlace.value = place
        }
    }

    fun onToggleCoordinateMode(enabled: Boolean) {
        _isCoordinateMode.value = enabled
        // Clear selection when switching modes to avoid confusion
        _selectedPlace.value = null
        _hasAnimatedToPlace.value = false
    }

    // **Content Selection State**
    private val _selectedCompactContent = MutableStateFlow(com.google.android.libraries.places.widget.PlaceDetailsCompactFragment.ALL_CONTENT)
    val selectedCompactContent: StateFlow<List<com.google.android.libraries.places.widget.PlaceDetailsCompactFragment.Content>> = _selectedCompactContent.asStateFlow()

    private val _selectedFullContent = MutableStateFlow(com.google.android.libraries.places.widget.PlaceDetailsFragment.STANDARD_CONTENT)
    val selectedFullContent: StateFlow<List<com.google.android.libraries.places.widget.PlaceDetailsFragment.Content>> = _selectedFullContent.asStateFlow()

    fun updateCompactContent(content: List<com.google.android.libraries.places.widget.PlaceDetailsCompactFragment.Content>) {
        _selectedCompactContent.value = content
    }

    fun updateFullContent(content: List<com.google.android.libraries.places.widget.PlaceDetailsFragment.Content>) {
        _selectedFullContent.value = content
    }

    fun onDismissPlace() {
        _selectedPlace.value = null
        _hasAnimatedToPlace.value = false
    }
}

================================================
FILE: PlaceDetailsCompose/src/main/res/drawable/close_button_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#80000000" /> <!-- Semi-transparent black -->
</shape>


================================================
FILE: PlaceDetailsCompose/src/main/res/drawable/ic_close.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0"
    android:tint="?attr/colorControlNormal">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>


================================================
FILE: PlaceDetailsCompose/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path
        android:fillColor="#3DDC84"
        android:pathData="M0,0h108v108h-108z" />
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,0L29,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,0L39,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,0L49,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,0L59,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,0L69,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,0L79,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M89,0L89,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M99,0L99,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,9L108,9"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,19L108,19"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,29L108,29"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,39L108,39"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,49L108,49"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,59L108,59"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,69L108,69"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,79L108,79"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,89L108,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,99L108,99"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,29L89,29"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,39L89,39"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,49L89,49"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,59L89,59"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,69L89,69"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,79L89,79"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,19L29,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,19L39,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,19L49,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,19L59,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,19L69,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,19L79,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
</vector>


================================================
FILE: PlaceDetailsCompose/src/main/res/drawable/ic_launcher_foreground.xml
================================================
<!--
 Copyright 2025 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.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="85.84757"
                android:endY="92.4963"
                android:startX="42.9492"
                android:startY="49.59793"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
        android:strokeWidth="1"
        android:strokeColor="#00000000" />
</vector>

================================================
FILE: PlaceDetailsCompose/src/main/res/drawable/outline_my_location_24.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M440,918L440,838Q315,824 225.5,734.5Q136,645 122,520L42,520L42,440L122,440Q136,315 225.5,225.5Q315,136 440,122L440,42L520,42L520,122Q645,136 734.5,225.5Q824,315 838,440L918,440L918,520L838,520Q824,645 734.5,734.5Q645,824 520,838L520,918L440,918ZM480,760Q596,760 678,678Q760,596 760,480Q760,364 678,282Q596,200 480,200Q364,200 282,282Q200,364 200,480Q200,596 282,678Q364,760 480,760ZM480,640Q414,640 367,593Q320,546 320,480Q320,414 367,367Q414,320 480,320Q546,320 593,367Q640,414 640,480Q640,546 593,593Q546,640 480,640ZM480,560Q513,560 536.5,536.5Q560,513 560,480Q560,447 536.5,423.5Q513,400 480,400Q447,400 423.5,423.5Q400,447 400,480Q400,513 423.5,536.5Q447,560 480,560ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
    
</vector>


================================================
FILE: PlaceDetailsCompose/src/main/res/drawable/outline_settings_24.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
      
    <path android:fillColor="@android:color/white" android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
    
</vector>


================================================
FILE: PlaceDetailsCompose/src/main/res/layout/place_details_fragment.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />


================================================
FILE: PlaceDetailsCompose/src/main/res/mipmap-anydpi/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: PlaceDetailsCompose/src/main/res/mipmap-anydpi/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: PlaceDetailsCompose/src/main/res/values/ids.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<resources>
    <item name="fragment_container_view" type="id" />
</resources>


================================================
FILE: PlaceDetailsCompose/src/main/res/values/strings.xml
================================================
<!--
     Copyright 2025 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

          https://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.
-->
<resources>
    <string name="app_name">Place Details Compose</string>
    <string name="map_id">YOUR_MAP_ID</string>
    <string name="location_permission_denied">Location permission denied</string>
    <string name="full_view">Full View</string>
    <string name="compact_view">Compact View</string>
    <string name="coords_mode">Coords Mode</string>
    <string name="poi_mode">POI Mode</string>
    <string name="select_fields">Select Fields</string>
    <string name="select_full_view_fields">Select Full View Fields</string>
    <string name="select_compact_view_fields">Select Compact View Fields</string>
    <string name="collapse_settings">Collapse Settings</string>
    <string name="expand_settings">Expand Settings</string>
    <string name="close">Close</string>
    <string name="done">Done</string>
    <string name="error_api_key_missing">API Key was not set in secrets.properties</string>
    <string name="error_map_id_missing">Map ID is not set. Some features may not work. See README for instructions.</string>
</resources>

================================================
FILE: PlaceDetailsCompose/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<resources>
    <style name="Theme.PlaceDetailsCompose" parent="Theme.AppCompat.Light.NoActionBar" />

    <style name="CustomizedPlaceDetailsTheme" parent="PlacesMaterialTheme">
        <item name="android:windowIsFloating">false</item>
        <item name="android:background">@android:color/holo_blue_light</item>
    </style>
</resources>

================================================
FILE: PlaceDetailsCompose/src/main/res/xml/backup_rules.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<!--
   Sample backup rules file; uncomment and customize as necessary.
   See https://developer.android.com/guide/topics/data/autobackup
   for details.
   Note: This file is ignored for devices older than API 31
   See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
    <!--
   <include domain="sharedpref" path="."/>
   <exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

================================================
FILE: PlaceDetailsCompose/src/main/res/xml/data_extraction_rules.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<!--
   Sample data extraction rules file; uncomment and customize as necessary.
   See https://developer.android.com/about/versions/12/backup-restore#xml-changes
   for details.
-->
<data-extraction-rules>
    <cloud-backup>
        <!-- TODO: Use <include> and <exclude> to control what is backed up.
        <include .../>
        <exclude .../>
        -->
    </cloud-backup>
    <!--
    <device-transfer>
        <include .../>
        <exclude .../>
    </device-transfer>
    -->
</data-extraction-rules>

================================================
FILE: PlaceDetailsUIKit/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties


================================================
FILE: PlaceDetailsUIKit/README.md
================================================
# **Place Details UI Kit Samples for Android**

This Android application provides two distinct demonstrations of the **Places** UI Kit for **Android
**, showcasing how to integrate and customize the PlaceDetailsCompactFragment.

1. **Simple Integration (MainActivity)**: A straightforward example of how to add the Place Details
   widget to an app. It focuses on handling map interactions, displaying the widget with a default
   set of content, and persisting its state across screen rotations using a ViewModel.
2. **Configurable Integration (ConfigurablePlaceDetailsActivity)**: A more advanced example that
   demonstrates how to dynamically configure the content sections displayed within the widget. It
   features a settings dialog, built with Jetpack Compose, that allows the user to select which
   place data fields (e.g., Photos, Rating, Website) they want to see.

Both samples demonstrate best practices for handling runtime permissions, the Android Activity
lifecycle (including configuration changes), and lifecycle-aware data loading to prevent common
crashes.

## **Features**

* **Google Map Integration**: Displays an interactive Google Map centered on the user's location.
* **POI Click Handling**: Detects clicks on POIs and retrieves their unique Place ID.
* **Place Details UI Kit**: Uses the modern PlaceDetailsCompactFragment to display rich details
  about a selected place.
* **Dynamic Orientation**: The PlaceDetailsCompactFragment automatically adjusts its layout between
  VERTICAL and HORIZONTAL based on the device's orientation.
* **Robust State Management**: Uses a ViewModel to retain the selected place and/or configuration
  across configuration changes (e.g., screen rotation), ensuring a seamless user experience.
* **Advanced Customization**: Features a custom "Synthwave" theme for the
  PlaceDetailsCompactFragment to demonstrate how easily the widget's appearance can be modified.
* **Dynamic Content Configuration**: The ConfigurablePlaceDetailsActivity shows how to let users
  choose which Place.Field sections are displayed in the widget at runtime.
* **Jetpack Compose Integration**: The content selection dialog is built using Jetpack
  Compose, showcasing its use within a View-based project.
* **Lifecycle-Aware Implementation**: Includes a robust solution to prevent common lifecycle-related
  crashes when loading the fragment.

## **Getting Started**

To build and run this sample application, you will need an API key from the Google Cloud Console.

### **Set Up Your API Key**

1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. Create a new project or select an existing one.
3. Enable the **Maps SDK for Android** and the **Places API**.
4. Create an API key. For security, it's highly recommended to restrict your API key to your Android
   app's package name and SHA-1 certificate fingerprint.
5. In the root directory of this project, create a file named secrets.properties. This file is
   already listed in .gitignore to prevent it from being checked into version control.
6. Add your API key to the secrets.properties file. The key should be assigned to both
   MAPS_API_KEY and PLACES_API_KEY:

```properties
   MAPS_API_KEY="YOUR_API_KEY_HERE"
   PLACES_API_KEY="YOUR_API_KEY_HERE"
```

### **Build and Run**

1. Open the project in Android Studio.
2. Let Gradle sync the project dependencies.
3. Run the app on an Android emulator or a physical device.

The app has two launcher activities. You can choose which one to run using the "Run/Debug
Configurations" dropdown in Android Studio.

* **MainActivity**: Launches the simple, non-configurable demo.
* **ConfigurablePlaceDetailsActivity**: Launches the advanced demo with content selection.

The app will request location permissions. Once granted, it will zoom to your current location.
Tapping on any POI on the map (e.g., a restaurant, park, or shop) will display the
PlaceDetailsCompactFragment at the bottom of the screen. In the configurable demo, a settings icon
allows you to customize the widget's content.

## **Code Highlights**

### **MainActivity.kt**

* **MainViewModel**: A simple ViewModel class defined at the top of the file. Its sole purpose is to
  store the selectedPlaceId so that it survives configuration changes.
* **onCreate()**:
    * Initializes the ActivityResultLauncher for handling location permission requests.
    * Initializes the Places SDK and the FusedLocationProviderClient.
    * Crucially, it checks if viewModel.selectedPlaceId is not null. If it has a value (meaning the
      app was rotated while a place was selected), it calls showPlaceDetailsFragment() to restore
      the view.
* **onPoiClick(poi: PointOfInterest)**:
    * This is the callback for when a user taps a POI on the map.
    * It saves the poi.placeId to the viewModel.
    * It then calls showPlaceDetailsFragment() to display the widget.
* **showPlaceDetailsFragment(placeId: String)**:
    * This is the core function for displaying the widget.
    * It dynamically determines the orientation (HORIZONTAL or VERTICAL) based on the device's
      current configuration.
    * It creates a new instance of PlaceDetailsCompactFragment, passing it the content to display,
      the orientation, and a custom theme (R.style.CustomizedPlaceDetailsTheme).
    * It sets a PlaceLoadListener to handle onSuccess and onFailure events. The UI (loading
      indicator and fragment visibility) is updated in these callbacks.
    * It adds the fragment to the FragmentContainerView using the FragmentManager.
    * **Important**: The call to fragment.loadWithPlaceId(placeId) is wrapped in
      binding.root.post { ... }. This is a key fix that prevents a
      kotlin.UninitializedPropertyAccessException crash by ensuring the fragment's view is fully
      created and attached before its data is loaded.

### **ConfigurablePlaceDetailsActivity.kt**

This activity demonstrates a more advanced use case where the content of the widget is
user-configurable.

* **ContentSelectionViewModel.kt**: This ViewModel is more complex. It holds both the
  selectedPlaceId and the state of the content configuration. It uses StateFlow to expose lists of
  selected and unselected content items, which the UI observes.
* **Content Configuration Dialog**:
    * The configure\_button FAB opens an AlertDialog.
    * The dialog's view (content\_selector\_dialog.xml) contains a ComposeView.
    * The UI of the dialog is built declaratively with Jetpack Compose in the DialogContent
      composable function. It displays two lists with sticky headers for "Selected" and "Unselected"
      content.
    * Clicking an item calls viewModel.toggleSelection(), which atomically updates the state flows,
      causing the Compose UI to automatically re-render.
* **showPlaceDetailsFragment(placeId: String)**:
    * This function is similar to the one in MainActivity, but with one key difference.
    * When creating the PlaceDetailsCompactFragment, it gets the list of content directly from the
      ViewModel: PlaceDetailsCompactFragment.newInstance(viewModel.selectedContent.value.map {
      it.content }, ...)
    * This ensures that whatever content the user has selected in the dialog is what the fragment
      will request and display.

### **Customization**

The custom "Synthwave" theme is defined in [`themes.xml`](app/src/main/res/values/themes.xml) and
[`colors.xml`](app/src/main/res/values/colors.xml). By overriding attributes like placesColorSurface,
placesColorPrimary, and placesTextAppearanceBodyMedium, you can completely change the look and feel
of the widget to match your app's branding.

```xml
<!-- In themes.xml -->  
<style name="CustomizedPlaceDetailsTheme" parent="PlacesMaterialTheme">  
<!-- Core Colors -->  
<item name="placesColorSurface">@color/synthwave_surface</item>  
<item name="placesColorPrimary">@color/synthwave_primary</item>  
...  
<!-- Typography -->  
<item name="placesTextAppearanceBodyMedium">@style/app_text_appearence_mono</item>  
</style>
```

This sample provides a complete and robust foundation for integrating the Places UI Kit into your
own applications.

================================================
FILE: PlaceDetailsUIKit/build.gradle.kts
================================================
/*
 * Copyright 2025 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.
 */

// The `plugins` block is where we apply Gradle plugins to this module.
// Plugins add new tasks and configurations to our build process.
plugins {
    id("places-demo.android.application")
    // This plugin enables Kotlin support in the Android project, allowing us to write code in Kotlin.
    alias(libs.plugins.kotlin.android)
    // This plugin from Google helps manage API keys and other secrets by reading them from a `secrets.properties`
    // file (which should be in .gitignore) and exposing them in the `BuildConfig` file at compile time.
    // This is crucial for keeping sensitive data out of version control.
    id("places-demo.secrets")
    // This plugin provides the necessary integration for using Jetpack Compose with the Kotlin compiler.
    alias(libs.plugins.kotlin.compose)
}

// The `android` block is where we configure all the Android-specific build options.
android {
    // The `namespace` is a unique identifier for the app's generated R class. It's also used
    // as the default `applicationId` if not specified in `defaultConfig`.
    namespace = "com.example.placedetailsuikit"

    defaultConfig {
        // `applicationId` is the unique identifier for the app on the Google Play Store and on the device.
        applicationId = "com.example.placedetailsuikit"
        // `minSdk` is the minimum API level required to run the app. Devices below this level cannot install it.
        minSdk = 27
        versionCode = 1
        versionName = "1.0"

        // Specifies the instrumentation runner for running Android tests.
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        // The `release` block configures settings for the release build of the app.
        release {
            // `isMinifyEnabled` enables code shrinking with R8 to reduce the app's size.
            // It's disabled here for simplicity in a sample app, but highly recommended for production.
            isMinifyEnabled = false
            // `proguardFiles` specifies the files that define the R8 shrinking and obfuscation rules.
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    kotlin {
        // Configures Kotlin-specific compiler options.
        compilerOptions {
            freeCompilerArgs.addAll(
                "-opt-in=kotlin.RequiresOptIn",
                "-Xannotation-default-target=param-property"
            )
        }
    }
    buildFeatures {
        // `viewBinding` generates a binding class for each XML layout file, providing a type-safe
        // way to access views without `findViewById`. This is used in the XML-based activities.
        viewBinding = true
        // `compose` enables Jetpack Compose for the project.
        compose = true
        // `buildConfig` generates a `BuildConfig` class that contains constants from the build configuration,
        // such as the API key from the secrets plugin.
        buildConfig = true
    }
    composeOptions {
        // Sets the version of the Kotlin compiler extension for Compose. This version must be
        // compatible with the Kotlin version used in the project.
        kotlinCompilerExtensionVersion = "1.5.1"
    }
}

// The `dependencies` block is where we declare all the external libraries the app needs.
// These are fetched from repositories like Maven Central and Google's Maven repository.
dependencies {
    // --- Core AndroidX & UI Libraries ---
    // These are foundational libraries for building modern Android apps.
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material) // For Material Design components (used in XML layouts).
    implementation(libs.androidx.activity)
    implementation(libs.androidx.constraintlayout)
    implementation(libs.androidx.fragment.ktx)
    implementation(libs.androidx.lifecycle.viewmodel.ktx) // For the ViewModel architecture component.

    // --- Google Play Services ---
    // These are the essential libraries for this sample, providing Maps and Places functionality.
    implementation(libs.google.maps.services) // The core SDK for embedding Google Maps.
    implementation(libs.places) // The SDK for the Places UI Kit (PlaceDetails fragments).
    implementation(libs.play.services.location) // Needed for the FusedLocationProviderClient to get the device's location.

    // --- Jetpack Compose ---
    // These libraries are for building UIs with Jetpack Compose.
    implementation(libs.androidx.material3) // The latest Material Design components for Compose.
    implementation(libs.androidx.activity.compose) // Integration between Activity and Compose.
    implementation(platform(libs.androidx.compose.bom)) // The Compose Bill of Materials (BOM) ensures all Compose libraries use compatible versions.
    implementation(libs.androidx.ui.tooling.preview) // For displaying @Preview composables in Android Studio.
    debugImplementation(libs.androidx.ui.tooling) // Provides tools for inspecting Compose UIs.

    // --- Testing Libraries ---
    // These libraries are for writing and running tests.
    // `testImplementation` is for local unit tests (running on the JVM).
    testImplementation(libs.junit)
    // `androidTestImplementation` is for instrumented tests (running on an Android device or emulator).
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core) // For UI testing with the View system.
    // AndroidX libraries for creating test rules and running tests.
    androidTestImplementation(libs.androidx.test.rules)
    androidTestImplementation(libs.androidx.test.runner)

    // --- Compose Testing ---
    // These are specific to testing Jetpack Compose UIs.
    androidTestImplementation(platform(libs.androidx.compose.bom)) // BOM for testing libraries.
    androidTestImplementation(libs.androidx.ui.test.junit4) // The main library for Compose UI tests.
    debugImplementation(libs.androidx.ui.test.manifest) // Provides a manifest for UI tests.
}


demoApp {
    mainActivity.set(".LauncherActivity")
}


================================================
FILE: PlaceDetailsUIKit/lint.xml
================================================
<!--
 Copyright 2025 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.
-->

<lint>
    <issue id="NotificationPermission" severity="ignore" />
</lint>


================================================
FILE: PlaceDetailsUIKit/local.defaults.properties
================================================
PLACES_API_KEY="YOUR_API_KEY"
MAPS_API_KEY="YOUR_API_KEY"

================================================
FILE: PlaceDetailsUIKit/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

================================================
FILE: PlaceDetailsUIKit/src/androidTest/java/com/example/placedetailsuikit/MainActivityInstrumentedTest.kt
================================================
// Copyright 2025 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.placedetailsuikit

import android.Manifest
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.PointOfInterest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
 * Instrumented tests for [MainActivity] to verify UI behavior and state management.
 * These tests run on an Android device or emulator.
 */
@RunWith(AndroidJUnit4::class)
class MainActivityInstrumentedTest {

    // A Rule to launch MainActivity before each test and clean it up afterward.
    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    // A Rule to grant location permissions before each test. This prevents the permission dialog
    // from interrupting the test flow.
    @get:Rule
    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )

    /**
     * Test to verify that the map container is displayed when the activity starts.
     */
    @Test
    fun test_mapIsDisplayedOnLaunch() {
        onView(withId(R.id.map_fragment)).check(matches(isDisplayed()))
    }

    /**
     * Test the full user flow:
     * 1. Simulate a POI click on the map.
     * 2. Verify the Place Details UI appears (with a loader first, then the content).
     * 3. Click the dismiss button.
     * 4. Verify the Place Details UI is hidden.
     */
    @Test
    fun test_poiClickAndDismissFlow() {
        // --- 1. Simulate a POI Click ---
        // We get the activity scenario and trigger onPoiClick directly on the UI thread.
        // This is a reliable way to test the UI logic without actually tapping the map.
        activityRule.scenario.onActivity { activity ->
            // A mock POI for "Google Sydney"
            val poi = PointOfInterest(
                LatLng(-33.865072, 151.1961474),
                "ChIJP3Sa8ziYEmsRUKgyFmh9AQM",
                "Google Sydney"
            )
            activity.onPoiClick(poi)
        }

        // --- 2. Verify UI After Click ---
        // The wrapper view containing the fragment should now be visible.
        onView(withId(R.id.place_details_wrapper)).check(matches(isDisplayed()))

        // Check for the loading indicator that is a child of our wrapper.
        // This avoids ambiguity with the loader inside the PlaceDetailsCompactFragment.
        onView(
            allOf(
                withId(R.id.loading_indicator_main),
                withParent(withId(R.id.place_details_wrapper))
            )
        ).check(matches(isDisplayed()))


        // The Place Details fragment loads data asynchronously. For a simple sample,
        // a short sleep is a straightforward way to wait for the UI to update.
        // For a production app, using Espresso Idling Resources is the recommended approach.
        Thread.sleep(3000) // Wait for 3 seconds for the network call to complete.

        // Check that our specific loader is now gone.
        onView(
            allOf(
                withId(R.id.loading_indicator_main),
                withParent(withId(R.id.place_details_wrapper))
            )
        ).check(matches(not(isDisplayed())))

        onView(withId(R.id.place_details_container)).check(matches(isDisplayed()))
        onView(withId(R.id.dismiss_button)).check(matches(isDisplayed()))

        // --- 3. Click the Dismiss Button ---
        onView(withId(R.id.dismiss_button)).perform(click())

        // --- 4. Verify UI After Dismiss ---
        // The wrapper view should now be hidden.
        onView(withId(R.id.place_details_wrapper)).check(matches(not(isDisplayed())))
    }

    /**
     * Test that the Place Details view's state is correctly restored after a configuration change
     * (e.g., screen rotation), thanks to the ViewModel.
     */
    @Test
    fun test_stateRestoresOnConfigurationChange() {
        // --- 1. Show the Place Details Fragment ---
        activityRule.scenario.onActivity { activity ->
            val poi = PointOfInterest(
                LatLng(-33.865072, 151.1961474),
                "ChIJP3Sa8ziYEmsRUKgyFmh9AQM",
                "Google Sydney"
            )
            activity.onPoiClick(poi)
        }

        // Wait for it to load.
        Thread.sleep(3000)
        onView(withId(R.id.place_details_wrapper)).check(matches(isDisplayed()))

        // --- 2. Recreate the Activity (Simulates Rotation) ---
        activityRule.scenario.recreate()

        // --- 3. Verify the UI is still visible ---
        // Add another wait after recreation for the fragment to reload and become visible.
        Thread.sleep(3000)

        // The wrapper should still be visible without needing another click because the
        // selected place ID was restored from the ViewModel.
        onView(withId(R.id.place_details_wrapper)).check(matches(isDisplayed()))
        onView(withId(R.id.place_details_container)).check(matches(isDisplayed()))
        onView(withId(R.id.dismiss_button)).check(matches(isDisplayed()))
    }
}


================================================
FILE: PlaceDetailsUIKit/src/androidTest/java/com/example/placedetailsuikit/compact/ConfigurablePlaceDetailsActivityInstrumentedTest.kt
================================================
// Copyright 2025 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.placedetailsuikit.compact

import android.Manifest
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import com.example.placedetailsuikit.R
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.PointOfInterest
import org.hamcrest.CoreMatchers
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
 * Instrumented tests for [ConfigurablePlaceDetailsActivity].
 * These tests verify the UI behavior related to the configuration dialog and state restoration.
 */
@RunWith(AndroidJUnit4::class)
class ConfigurablePlaceDetailsActivityInstrumentedTest {

    /**
     * A rule to launch [ConfigurablePlaceDetailsActivity] and interact with its Compose content.
     */
    @get:Rule
    val composeTestRule = createAndroidComposeRule<ConfigurablePlaceDetailsActivity>()

    /**
     * A rule to grant location permissions before each test, preventing system dialogs
     * from interfering with the tests.
     */
    @get:Rule
    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )

    /**
     * Tests that the configuration dialog opens, displays content correctly,
     * and can be dismissed.
     */
    @Test
    fun test_configureDialogOpensAndDismisses() {
        // 1. Click the "Configure" FAB to open the dialog
        Espresso.onView(ViewMatchers.withId(R.id.configure_button)).perform(ViewActions.click())

        // 2. Verify that the Compose-based dialog content is displayed
        // We check for the sticky headers to confirm the LazyColumn is there.
        composeTestRule.onNodeWithText("Selected Content").assertIsDisplayed()
        composeTestRule.onNodeWithText("Unselected Content").assertIsDisplayed()

        // Check for a specific item in the list
        composeTestRule.onNodeWithText("Rating").assertIsDisplayed()

        // 3. Click the "Close" button on the AlertDialog
        Espresso.onView(ViewMatchers.withText("Close")).perform(ViewActions.click())

        // 4. Verify the dialog is gone by checking that its content is no longer visible
        composeTestRule.onNodeWithText("Selected Content").assertDoesNotExist()
    }

    /**
     * Tests that toggling an item in the dialog and then rotating the screen
     * preserves the selection state.
     */
    @Test
    fun test_selectionStatePersistsOnConfigurationChange() {
        // 1. Open the configuration dialog
        Espresso.onView(ViewMatchers.withId(R.id.configure_button)).perform(ViewActions.click())

        // 2. Toggle an item (e.g., move "Rating" from selected to unselected)
        composeTestRule.onNodeWithText("Rating").performClick()

        // After the click, "Rating" should now be under the "Unselected Content" header.
        // We can verify this by checking its new position relative to the headers.
        composeTestRule.onNodeWithText("Rating").assertIsDisplayed()


        // 3. Close the dialog
        Espresso.onView(ViewMatchers.withText("Close")).perform(ViewActions.click())

        // 4. Recreate the activity to simulate a screen rotation
        composeTestRule.activityRule.scenario.recreate()

        // 5. Re-open the dialog and verify the state was restored
        Espresso.onView(ViewMatchers.withId(R.id.configure_button)).perform(ViewActions.click())

        // Check that "Rating" is still in the unselected list.
        composeTestRule.onNodeWithText("Unselected Content").assertIsDisplayed()
        composeTestRule.onNodeWithText("Rating").assertIsDisplayed()
    }

    /**
     * Test the full user flow: click POI, check that the Place Details card is displayed,
     * and dismiss it. This confirms the activity's core functionality.
     */
    @Test
    fun test_poiClickAndDismissFlow() {
        // 1. Simulate a POI Click
        composeTestRule.activityRule.scenario.onActivity { activity ->
            val poi = PointOfInterest(
                LatLng(-33.865072, 151.1961474),
                "ChIJP3Sa8ziYEmsRUKgyFmh9AQM",
                "Google Sydney"
            )
            activity.onPoiClick(poi)
        }

        // 2. Verify UI After Click
        // The wrapper view should be visible, and the loader should be showing initially.
        Espresso.onView(ViewMatchers.withId(R.id.place_details_wrapper))
            .check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
        Espresso.onView(ViewMatchers.withId(R.id.loading_indicator_configurable))
            .check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

        // Wait for the place to load. For a real app, use Espresso Idling Resources.
        Thread.sleep(3000)

        // The loader should be gone, and the fragment container should be visible.
        Espresso.onView(ViewMatchers.withId(R.id.loading_indicator_configurable))
            .check(ViewAssertions.matches(CoreMatchers.not(ViewMatchers.isDisplayed())))
        Espresso.onView(ViewMatchers.withId(R.id.place_details_container))
            .check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
        Espresso.onView(ViewMatchers.withId(R.id.dismiss_button))
            .check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

        // 3. Click the Dismiss Button
        Espresso.onView(ViewMatchers.withId(R.id.dismiss_button)).perform(ViewActions.click())

        // 4. Verify UI After Dismiss
        Espresso.onView(ViewMatchers.withId(R.id.place_details_wrapper))
            .check(ViewAssertions.matches(CoreMatchers.not(ViewMatchers.isDisplayed())))
    }
}

================================================
FILE: PlaceDetailsUIKit/src/androidTest/java/com/example/placedetailsuikit/full/FullConfigurablePlaceDetailsActivityInstrumentedTest.kt
================================================
// Copyright 2025 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.placedetailsuikit.full

import android.Manifest
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import com.example.placedetailsuikit.R
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.PointOfInterest
import org.hamcrest.CoreMatchers.not
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
 * Instrumented tests for [FullConfigurablePlaceDetailsActivity].
 * These tests verify the UI behavior related to the configuration dialog and state restoration
 * for the full Place Details Fragment.
 */
@RunWith(AndroidJUnit4::class)
class FullConfigurablePlaceDetailsActivityInstrumentedTest {

    /**
     * A rule to launch [FullConfigurablePlaceDetailsActivity] and interact with its Compose content.
     */
    @get:Rule
    val composeTestRule = createAndroidComposeRule<FullConfigurablePlaceDetailsActivity>()

    /**
     * A rule to grant location permissions before each test, preventing system dialogs
     * from interfering with the tests.
     */
    @get:Rule
    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )

    /**
     * Tests that the configuration dialog opens, displays content correctly,
     * and can be dismissed.
     */
    @Test
    fun test_configureDialogOpensAndDismisses() {
        // 1. Click the "Configure" FAB to open the dialog
        onView(withId(R.id.configure_button)).perform(click())

        // 2. Verify that the Compose-based dialog content is displayed
        // We check for the sticky headers to confirm the LazyColumn is there.
        composeTestRule.onNodeWithText("Selected Content").assertIsDisplayed()
        composeTestRule.onNodeWithText("Unselected Content").assertIsDisplayed()

        // Check for a specific item in the list
        composeTestRule.onNodeWithText("Rating").assertIsDisplayed()

        // 3. Click the "Close" button on the AlertDialog
        onView(withText("Close")).perform(click())

        // 4. Verify the dialog is gone by checking that its content is no longer visible
        composeTestRule.onNodeWithText("Selected Content").assertDoesNotExist()
    }

    /**
     * Tests that toggling an item in the dialog and then rotating the screen
     * preserves the selection state.
     */
    @Test
    fun test_selectionStatePersistsOnConfigurationChange() {
        // 1. Open the configuration dialog
        onView(withId(R.id.configure_button)).perform(click())

        // 2. Toggle an item (e.g., move "Rating" from selected to unselected)
        composeTestRule.onNodeWithText("Rating").performClick()

        // After the click, "Rating" should now be under the "Unselected Content" header.
        // We can verify this by checking its new position relative to the headers.
        // The item should still be displayed, but its "location" in the list has changed conceptually.
        // The check that follows is more specific to its new list.

        // 3. Close the dialog
        onView(withText("Close")).perform(click())

        // 4. Recreate the activity to simulate a screen rotation
        composeTestRule.activityRule.scenario.recreate()

        // 5. Re-open the dialog and verify the state was restored
        onView(withId(R.id.configure_button)).perform(click())

        // Check that "Rating" is still in the unselected list.
        composeTestRule.onNodeWithText("Unselected Content").assertIsDisplayed()
        composeTestRule.onNodeWithText("Rating").assertIsDisplayed()
    }

    /**
     * Test the full user flow: click POI, check that the Place Details card is displayed,
     * and dismiss it. This confirms the activity's core functionality.
     */
    @Test
    fun test_poiClickAndDismissFlow() {
        // 1. Simulate a POI Click
        composeTestRule.activityRule.scenario.onActivity { activity ->
            val poi = PointOfInterest(
                LatLng(-33.865072, 151.1961474),
                "ChIJP3Sa8ziYEmsRUKgyFmh9AQM",
                "Google Sydney"
            )
            activity.onPoiClick(poi)
        }

        // 2. Verify UI After Click
        // The wrapper view should be visible, and the loader should be showing initially.
        onView(withId(R.id.place_details_wrapper)).check(matches(isDisplayed()))
        onView(withId(R.id.loading_indicator_configurable)).check(matches(isDisplayed()))

        // Wait for the place to load. For a real app, use Espresso Idling Resources.
        Thread.sleep(3000)

        // The loader should be gone, and the fragment container should be visible.
        onView(withId(R.id.loading_indicator_configurable)).check(matches(not(isDisplayed())))
        onView(withId(R.id.place_details_container)).check(matches(isDisplayed()))
        onView(withId(R.id.dismiss_button)).check(matches(isDisplayed()))

        // 3. Click the Dismiss Button
        onView(withId(R.id.dismiss_button)).perform(click())

        // 4. Verify UI After Dismiss
        onView(withId(R.id.place_details_wrapper)).check(matches(not(isDisplayed())))
    }
}


================================================
FILE: PlaceDetailsUIKit/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright 2025 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.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar"
        tools:targetApi="36">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}"/>

        <activity
            android:name=".LauncherActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".MainActivity"
            android:exported="true"/>

        <activity
            android:name=".compact.ConfigurablePlaceDetailsActivity"
            android:theme="@style/Theme.MaterialComponents.Light.NoActionBar"
            android:exported="true"/>

        <activity
            android:name=".full.FullConfigurablePlaceDetailsActivity"
            android:theme="@style/Theme.MaterialComponents.Light.NoActionBar"
            android:exported="true"/>

    </application>

</manifest>

================================================
FILE: PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/LauncherActivity.kt
================================================
// Copyright 2025 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.placedetailsuikit

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import com.example.placedetailsuikit.compact.ConfigurablePlaceDetailsActivity
import com.example.placedetailsuikit.full.FullConfigurablePlaceDetailsActivity
import com.example.placedetailsuikit.ui.theme.PlaceDetailsUIKitTheme

class LauncherActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            PlaceDetailsUIKitTheme {
                LauncherScreen()
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LauncherScreen() {
    val context = LocalContext.current
    Scaffold(
        topBar = {
            TopAppBar(title = { Text("Place Details UIKit Demos") })
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(innerPadding),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Button(onClick = {
                context.startActivity(Intent(context, MainActivity::class.java))
            }) {
                Text("Main Activity")
            }
            Button(onClick = {
                context.startActivity(Intent(context, ConfigurablePlaceDetailsActivity::class.java))
            }) {
                Text("Compact Place Details")
            }
            Button(onClick = {
                context.startActivity(Intent(context, FullConfigurablePlaceDetailsActivity::class.java))
            }) {
                Text("Full Place Details")
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun LauncherScreenPreview() {
    PlaceDetailsUIKitTheme {
        LauncherScreen()
    }
}

================================================
FILE: PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/MainActivity.kt
================================================
// Copyright 2025 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.

// [START placessdkandroid_place_details_ui_kit_add_place_details_component_full]

package com.example.placedetailsuikit

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.location.Location
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.lifecycle.ViewModel
import com.example.placedetailsuikit.databinding.ActivityMainBinding
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.LatLng
import com.google.android.gms.maps.model.PointOfInterest
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.PlaceDetailsCompactFragment
import com.google.android.libraries.places.widget.PlaceLoadListener
import com.google.android.libraries.places.widget.model.Orientation

private const val TAG = "PlacesUiKit"

/**
 * A simple ViewModel to store UI state that needs to survive configuration changes.
 * In this case, it holds the ID of the selected place. Using a ViewModel is good practice
 * as it prevents data loss during events like screen rotation, ensuring a
 * seamless user experience.
 */
class MainViewModel : ViewModel() {
    var selectedPlaceId: String? = null
}

/**
 * This activity serves as a basic example of integrating the Place Details UI Kit.
 * It demonstrates the fundamental steps required:
 * 1. Setting up a Google Map.
 * 2. Requesting location permissions to center the map.
 * 3. Handling clicks on Points of Interest (POIs) to get a Place ID.
 * 4. Using the Place ID to load and display place details in a [PlaceDetailsCompactFragment].
 */
class MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnPoiClickListener {
    // ViewBinding provides type-safe access to views defined in the XML layout,
    // eliminating the need for `findViewById` and preventing null pointer exceptions.
    private lateinit var binding: ActivityMainBinding
    private var googleMap: GoogleMap? = null

    // The FusedLocationProviderClient is the main entry point for interacting with the
    // fused location provider, which intelligently manages the underlying location technologies.
    private lateinit var fusedLocationClient: FusedLocationProviderClient

    // Using registerForActivityResult is the modern, recommended approach for handling
    // permission requests. It decouples the request from the handling logic, making the
    // code cleaner and easier to manage compared to the older `onRequestPermissionsResult` callback.
    private lateinit var requestPermissionLauncher: ActivityResultLauncher<Array<String>>

    // The `by viewModels()` delegate provides a lazy-initialized ViewModel scoped to this Activity.
    // This ensures that we get the same ViewModel instance across configuration changes.
    private val viewModel: MainViewModel by viewModels()

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

        // The ActivityResultLauncher is initialized here. The lambda defines the callback
        // that will be executed once the user responds to the permission dialog.
        requestPermissionLauncher =
            registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
                // We check if either fine or coarse location permission was granted.
                if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true || permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true) {
                    Log.d(TAG, "Location permission granted by user.")
                    fetchLastLocation()
                } else {
                    // If permission is denied, we inform the user and default to a known location.
                    // This ensures the app remains functional even without location access.
                    Log.d(TAG, "Location permission denied by user.")
                    Toast.makeText(
                        this,
                        "Location permission denied. Showing default location.",
                        Toast.LENGTH_LONG
                    ).show()
                    moveToSydney()
                }
            }

        // enableEdgeToEdge() allows the app to draw behind the system bars for a more immersive experience.
        enableEdgeToEdge()
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.dismissButton.setOnClickListener {
            dismissPlaceDetails()
        }

        // --- Crucial: Initialize Places SDK ---
        // It's essential to initialize the Places SDK before making any other Places API calls.
        // This should ideally be done once, for example, in the Application's `onCreate`.
        val apiKey = BuildConfig.PLACES_API_KEY
        if (apiKey.isEmpty() || apiKey == "YOUR_API_KEY") {
            // A valid API key is required for the Places SDK to function.
            Log.e(TAG, "No api key")
            Toast.makeText(
                this,
                "Add your own API_KEY in local.properties",
                Toast.LENGTH_LONG
            ).show()
            finish()
            return
        }

        // `initializeWithNewPlacesApiEnabled` is used to opt-in to the new SDK version.
        Places.initializeWithNewPlacesApiEnabled(applicationContext, apiKey)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        // ------------------------------------

        // The SupportMapFragment is the container for the map. `getMapAsync` allows us to
        // work with the GoogleMap object via a callback once it's fully initialized.
        val mapFragment =
            supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment?
        mapFragment?.getMapAsync(this)

        // This block handles restoration after a configuration change (e.g., screen rotation).
        // If a place was selected before the rotation, its ID is stored in the ViewModel.
        // We use this ID to immediately show the details fragment again.
        if (viewModel.selectedPlaceId != null) {
            viewModel.selectedPlaceId?.let { placeId ->
                Log.d(TAG, "Restoring PlaceDetailsFragment for place ID: $placeId")
                showPlaceDetailsFragment(placeId)
            }
        }
    }

    /**
     * This callback is triggered when the GoogleMap object is ready to be used.
     * All map setup logic should be placed here.
     */
    override fun onMapReady(map: GoogleMap) {
        Log.d(TAG, "Map is ready")
        googleMap = map
        // Setting the OnPoiClickListener allows us to capture user taps on points of interest.
        googleMap?.setOnPoiClickListener(this)

        // After the map is ready, we determine the initial camera position based on location permissions.
        if (isLocationPermissionGranted()) {
            fetchLastLocation()
        } else {
            requestLocationPermissions()
        }
    }

    /**
     * A helper function to centralize the check for location permissions.
     */
    private fun isLocationPermissionGranted(): Boolean {
        return ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED ||
                ActivityCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ) == PackageManager.PERMISSION_GRANTED
    }

    /**
     * This function triggers the permission request flow. The result is handled by the
     * ActivityResultLauncher defined in `onCreate`.
     */
    private fun requestLocationPermissions() {
        Log.d(TAG, "Requesting location permissions.")
        requestPermissionLauncher.launch(
            arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            )
        )
    }

    /**
     * Fetches the device's last known location. This is a fast and battery-efficient way
     * to get a location fix. It should only be called after verifying permissions.
     */
    @SuppressLint("MissingPermission")
    private fun fetchLastLocation() {
        // Double-checking permissions here is a good practice, although the call sites are already guarded.
        if (isLocationPermissionGranted()) {
            fusedLocationClient.lastLocation
                .addOnSuccessListener { location: Location? ->
                    if (location != null) {
                        val userLocation = LatLng(location.latitude, location.longitude)
                        googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(userLocation, 13f))
                        Log.d(TAG, "Moved to user's last known location.")
                    } else {
                        // `lastLocation` can be null if the location has never been recorded.
                        // In this case, we fall back to a default location.
                        Log.d(TAG, "Last known location is null. Falling back to Sydney.")
                        moveToSydney()
                    }
                }
                .addOnFailureListener {
                    // This listener handles errors in the location fetching process.
                    Log.e(TAG, "Failed to get location.", it)
                    moveToSydney()
                }
        }
    }

    /**
     * Moves the map camera to a default, hardcoded location (Sydney).
     * This serves as a reliable fallback.
     */
    private fun moveToSydney() {
        val sydney = LatLng(-33.8688, 151.2093)
        googleMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 13f))
        Log.d(TAG, "Moved to Sydney")
    }

    /**
     * This is the callback for the `OnPoiClickListener`. It's triggered when a user
     * taps a POI on the map.
     */
    override fun onPoiClick(poi: PointOfInterest) {
        val placeId = poi.placeId
        Log.d(TAG, "Place ID: $placeId")

        // We save the selected place ID to the ViewModel. This is critical for surviving
        // configuration changes. If the user rotates the screen now, the `onCreate`
        // method will be able to restore the place details view.
        viewModel.selectedPlaceId = placeId
        showPlaceDetailsFragment(placeId)
    }

    /**
     * This function is the core of the integration. It creates, configures, and displays
     * the [PlaceDetailsCompactFragment].
     * @param placeId The unique identifier for the place to be displayed.
     */
    private fun showPlaceDetailsFragment(placeId: String) {
        Log.d(TAG, "Showing PlaceDetailsFragment for place ID: $placeId")

        // We manage the visibility of UI elements to provide feedback to the user.
        // The wrapper is shown, and a loading indicator is displayed while the data is fetched.
        binding.placeDetailsWrapper.visibility = View.VISIBLE
        binding.dismissButton.visibility = View.GONE
        binding.placeDetailsContainer.visibility = View.GONE
        binding.loadingIndicatorMain.visibility = View.VISIBLE

        // The Place Details widget can be displayed vertically or horizontally.
        // We dynamically choose the orientation based on the device's current configuration.
        val orientation =
            if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                Orientation.HORIZONTAL
            } else {
                Orientation.VERTICAL
            }

        // [START placessdkandroid_place_details_ui_kit_add_place_details_component_snippet]
        
        // We create a new instance of the fragment using its factory method.
        // We can specify which content to show, the orientation, and a custom theme.
        val fragment = PlaceDetailsCompactFragment.newInstance(
            PlaceDetailsCompactFragment.ALL_CONTENT, // Show all available content.
            orientation,
            R.style.CustomizedPlaceDetailsTheme,
        ).apply {
            // The PlaceLoadListener provides callbacks for when the place data is successfully
            // loaded or when an error occurs. This is where we update our UI state.
            setPlaceLoadListener(object : PlaceLoadListener {
                override fun onSuccess(place: Place) {
                    Log.d(TAG, "Place loaded: ${place.id}")
                    // Once the data is loaded, we hide the loading indicator and show the fragment.
                    binding.loadingIndicatorMain.visibility = View.GONE
                    binding.placeDe
Download .txt
gitextract_jubtpusk/

├── .gemini/
│   ├── config.yaml
│   ├── skills/
│   │   └── places-android/
│   │       └── SKILL.md
│   └── styleguide.md
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── support_request.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── pull_request_template.md
│   ├── dependabot.yml
│   ├── header-checker-lint.yml
│   ├── pull_request_template.md
│   ├── snippet-bot.yml
│   ├── stale.yml
│   ├── sync-repo-settings.yaml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── PlaceDetailsCompose/
│   ├── .gitignore
│   ├── ARCHITECTURE.md
│   ├── README.md
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placedetailscompose/
│           │               ├── MainActivity.kt
│           │               ├── PlaceDetailsComposeApplication.kt
│           │               ├── repository/
│           │               │   └── LocationRepository.kt
│           │               ├── ui/
│           │               │   ├── map/
│           │               │   │   ├── MapScreen.kt
│           │               │   │   ├── PlaceContentSelectionDialog.kt
│           │               │   │   └── PlaceDetailsView.kt
│           │               │   └── theme/
│           │               │       ├── Color.kt
│           │               │       ├── Theme.kt
│           │               │       ├── Typography.kt
│           │               │       └── Utils.kt
│           │               └── viewmodels/
│           │                   └── MapViewModel.kt
│           └── res/
│               ├── drawable/
│               │   ├── close_button_background.xml
│               │   ├── ic_close.xml
│               │   ├── ic_launcher_background.xml
│               │   ├── ic_launcher_foreground.xml
│               │   ├── outline_my_location_24.xml
│               │   └── outline_settings_24.xml
│               ├── layout/
│               │   └── place_details_fragment.xml
│               ├── mipmap-anydpi/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               ├── values/
│               │   ├── ids.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               └── xml/
│                   ├── backup_rules.xml
│                   └── data_extraction_rules.xml
├── PlaceDetailsUIKit/
│   ├── .gitignore
│   ├── README.md
│   ├── build.gradle.kts
│   ├── lint.xml
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── example/
│       │               └── placedetailsuikit/
│       │                   ├── MainActivityInstrumentedTest.kt
│       │                   ├── compact/
│       │                   │   └── ConfigurablePlaceDetailsActivityInstrumentedTest.kt
│       │                   └── full/
│       │                       └── FullConfigurablePlaceDetailsActivityInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── example/
│       │   │           └── placedetailsuikit/
│       │   │               ├── LauncherActivity.kt
│       │   │               ├── MainActivity.kt
│       │   │               ├── compact/
│       │   │               │   ├── ConfigurablePlaceDetailsActivity.kt
│       │   │               │   └── ContentSelectionViewModel.kt
│       │   │               ├── full/
│       │   │               │   ├── FullConfigurablePlaceDetailsActivity.kt
│       │   │               │   └── FullContentSelectionViewModel.kt
│       │   │               └── ui/
│       │   │                   └── theme/
│       │   │                       ├── Color.kt
│       │   │                       ├── Theme.kt
│       │   │                       └── Type.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── close_button_background.xml
│       │       │   ├── ic_close.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── ic_launcher_foreground.xml
│       │       │   ├── outline_my_location_24.xml
│       │       │   └── outline_settings_24.xml
│       │       ├── font/
│       │       │   └── custom_font.xml
│       │       ├── layout/
│       │       │   ├── activity_configurable_map.xml
│       │       │   ├── activity_full_configurable_map.xml
│       │       │   ├── activity_main.xml
│       │       │   └── content_selector_dialog.xml
│       │       ├── mipmap-anydpi/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── strings.xml
│       │       │   └── themes.xml
│       │       ├── values-night/
│       │       │   └── themes.xml
│       │       └── xml/
│       │           ├── backup_rules.xml
│       │           └── data_extraction_rules.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── example/
│                       └── placedetailsuikit/
│                           └── ExampleUnitTest.kt
├── PlacesUIKit3D/
│   ├── .gitignore
│   ├── README.md
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placesuikit3d/
│           │               ├── Landmark.kt
│           │               ├── LandmarkList.kt
│           │               ├── MainActivity.kt
│           │               ├── MainViewModel.kt
│           │               ├── Maps3DPlacesApplication.kt
│           │               ├── common/
│           │               │   ├── ActiveMapObject.kt
│           │               │   ├── Map3dViewModel.kt
│           │               │   └── MapObject.kt
│           │               ├── ui/
│           │               │   └── theme/
│           │               │       ├── Color.kt
│           │               │       ├── Theme.kt
│           │               │       └── Type.kt
│           │               └── utils/
│           │                   ├── CameraUpdate.kt
│           │                   ├── Units.kt
│           │                   └── Utilities.kt
│           └── res/
│               ├── drawable/
│               │   ├── close_button_background.xml
│               │   ├── ic_close.xml
│               │   ├── ic_launcher_background.xml
│               │   ├── ic_launcher_foreground.xml
│               │   ├── ic_my_location.xml
│               │   ├── loader_background.xml
│               │   └── outline_my_location_24.xml
│               ├── font/
│               │   └── custom_font.xml
│               ├── layout/
│               │   └── activity_main.xml
│               ├── mipmap-anydpi/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               ├── values/
│               │   ├── colors.xml
│               │   ├── strings.xml
│               │   └── themes.xml
│               └── xml/
│                   ├── backup_rules.xml
│                   └── data_extraction_rules.xml
├── README.md
├── SECURITY.md
├── build-logic/
│   ├── convention/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── main/
│   │           └── kotlin/
│   │               ├── places-demo.android.application.gradle.kts
│   │               └── places-demo.secrets.gradle.kts
│   └── settings.gradle.kts
├── build.gradle.kts
├── demo-java/
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placesdemo/
│           │               ├── AutocompleteAddressActivity.java
│           │               ├── CurrentPlaceActivity.java
│           │               ├── FieldSelector.java
│           │               ├── MainActivity.java
│           │               ├── PlaceAutocompleteActivity.java
│           │               ├── PlaceDetailsAndPhotosActivity.java
│           │               ├── PlaceIsOpenActivity.java
│           │               ├── PlacesDemoApplication.java
│           │               ├── StringUtil.java
│           │               ├── model/
│           │               │   ├── AddressType.java
│           │               │   ├── AutocompleteEditText.java
│           │               │   ├── Bounds.java
│           │               │   ├── GeocodingResult.java
│           │               │   ├── Geometry.java
│           │               │   ├── LocationType.java
│           │               │   └── PlusCode.java
│           │               └── programmatic_autocomplete/
│           │                   ├── LatLngAdapter.java
│           │                   ├── PlacePredictionAdapter.java
│           │                   └── ProgrammaticAutocompleteToolbarActivity.java
│           └── res/
│               ├── drawable/
│               │   └── ic_search_black_24dp.xml
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── activity_programmatic_autocomplete.xml
│               │   ├── autocomplete_address_activity.xml
│               │   ├── autocomplete_address_map.xml
│               │   ├── current_place_activity.xml
│               │   ├── place_autocomplete_activity.xml
│               │   ├── place_details_and_photos_activity.xml
│               │   ├── place_is_open_activity.xml
│               │   └── place_prediction_item.xml
│               ├── menu/
│               │   └── menu.xml
│               ├── raw/
│               │   └── style_json.json
│               └── values/
│                   ├── dimens.xml
│                   └── strings.xml
├── demo-kotlin/
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── placesdemo/
│           │               ├── AutocompleteAddressActivity.kt
│           │               ├── BaseActivity.kt
│           │               ├── CurrentPlaceActivity.kt
│           │               ├── FieldSelector.kt
│           │               ├── MainActivity.kt
│           │               ├── PlaceAutocompleteActivity.kt
│           │               ├── PlaceDetailsAndPhotosActivity.kt
│           │               ├── PlaceIsOpenActivity.kt
│           │               ├── PlacesDemoApplication.kt
│           │               ├── PlacesDemoGlideModule.kt
│           │               ├── StringUtil.kt
│           │               ├── model/
│           │               │   ├── AddressType.kt
│           │               │   ├── AutocompleteEditText.kt
│           │               │   ├── Bounds.kt
│           │               │   ├── GeocodingResult.kt
│           │               │   ├── Geometry.kt
│           │               │   ├── LocationType.kt
│           │               │   └── PlusCode.kt
│           │               └── programmatic_autocomplete/
│           │                   ├── LatLngAdapter.kt
│           │                   ├── PlacePredictionAdapter.kt
│           │                   └── ProgrammaticAutocompleteGeocodingActivity.kt
│           └── res/
│               ├── drawable/
│               │   ├── ic_exit.xml
│               │   ├── ic_exit_to_app_black_24dp.xml
│               │   └── ic_search_black_24dp.xml
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── activity_programmatic_autocomplete.xml
│               │   ├── autocomplete_address_activity.xml
│               │   ├── autocomplete_address_map.xml
│               │   ├── current_place_activity.xml
│               │   ├── place_autocomplete_activity.xml
│               │   ├── place_details_and_photos_activity.xml
│               │   ├── place_is_open_activity.xml
│               │   └── place_prediction_item.xml
│               ├── menu/
│               │   ├── main_activity_menu.xml
│               │   └── menu.xml
│               ├── raw/
│               │   └── style_json.json
│               └── values/
│                   ├── dimens.xml
│                   └── strings.xml
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── kotlin-demos/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── local.defaults.properties
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── google/
│           │           └── places/
│           │               └── android/
│           │                   └── ktx/
│           │                       └── demo/
│           │                           ├── AutocompleteDemoActivity.kt
│           │                           ├── Demo.kt
│           │                           ├── DemoActivity.kt
│           │                           ├── DemoApplication.kt
│           │                           ├── PlacesPhotoDemoActivity.kt
│           │                           ├── PlacesPhotoViewModel.kt
│           │                           ├── PlacesSearchDemoActivity.kt
│           │                           ├── PlacesSearchEvent.kt
│           │                           ├── PlacesSearchViewModel.kt
│           │                           ├── inject/
│           │                           │   └── DemoModule.kt
│           │                           └── ui/
│           │                               ├── Color.kt
│           │                               ├── Theme.kt
│           │                               └── Type.kt
│           └── res/
│               ├── drawable/
│               │   ├── ic_launcher_background.xml
│               │   └── ic_launcher_foreground.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── local.defaults.properties
├── settings.gradle.kts
└── snippets/
    ├── .gitignore
    ├── build.gradle.kts
    ├── local.defaults.properties
    ├── proguard-rules.pro
    └── src/
        └── main/
            ├── AndroidManifest.xml
            ├── java/
            │   └── com/
            │       └── google/
            │           └── places/
            │               ├── CurrentPlaceActivity.java
            │               ├── GetStartedActivity.java
            │               ├── JavaMainActivity.java
            │               ├── PlaceAutocompleteActivity.java
            │               ├── PlaceDetailsActivity.java
            │               ├── PlaceIsOpenActivity.java
            │               ├── PlacePhotosActivity.java
            │               ├── PlacesIconActivity.java
            │               ├── data/
            │               │   └── PlaceIdProvider.java
            │               └── kotlin/
            │                   ├── CurrentPlaceActivity.kt
            │                   ├── GetStartedActivity.kt
            │                   ├── KotlinMainActivity.kt
            │                   ├── MainApplication.kt
            │                   ├── PlaceAutocompleteActivity.kt
            │                   ├── PlaceDetailsActivity.kt
            │                   ├── PlaceIsOpenActivity.kt
            │                   ├── PlacePhotosActivity.kt
            │                   └── PlacesIconActivity.kt
            └── res/
                ├── drawable/
                │   └── ic_launcher_background.xml
                ├── drawable-v24/
                │   └── ic_launcher_foreground.xml
                ├── layout/
                │   ├── activity_current_place.xml
                │   ├── activity_main.xml
                │   ├── activity_place_autocomplete.xml
                │   ├── activity_place_details.xml
                │   ├── activity_place_is_open.xml
                │   ├── activity_place_photos.xml
                │   ├── activity_places_icon.xml
                │   ├── list_item_activity.xml
                │   └── list_item_place.xml
                ├── mipmap-anydpi-v26/
                │   ├── ic_launcher.xml
                │   └── ic_launcher_round.xml
                ├── values/
                │   ├── colors.xml
                │   ├── strings.xml
                │   └── styles.xml
                └── values-v27/
                    └── styles.xml
Download .txt
SYMBOL INDEX (207 symbols across 28 files)

FILE: demo-java/src/main/java/com/example/placesdemo/AutocompleteAddressActivity.java
  class AutocompleteAddressActivity (line 70) | @SuppressWarnings("FieldCanBeLocal")
    method onActivityResult (line 113) | @Override
    method onCreate (line 119) | @Override
    method startAutocompleteIntent (line 151) | private void startAutocompleteIntent() {
    method onMapReady (line 168) | @Override
    method fillInAddress (line 188) | private void fillInAddress(Place place) {
    method showMap (line 250) | private void showMap(Place place) {
    method updateMap (line 279) | private void updateMap(LatLng latLng) {
    method saveForm (line 287) | private void saveForm() {
    method clearForm (line 300) | private void clearForm() {
    method checkLocationPermissions (line 332) | private void checkLocationPermissions() {
    method getAndCompareLocations (line 343) | @SuppressLint("MissingPermission")

FILE: demo-java/src/main/java/com/example/placesdemo/CurrentPlaceActivity.java
  class CurrentPlaceActivity (line 53) | public class CurrentPlaceActivity extends AppCompatActivity {
    method checkLocationPermissions (line 80) | @SuppressLint("MissingPermission")
    method onCreate (line 91) | @Override
    method onSaveInstanceState (line 137) | @Override
    method findCurrentPlaceWithPermissions (line 148) | @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
    method getPlaceFields (line 174) | private List<Field> getPlaceFields() {
    method isDisplayRawResultsChecked (line 182) | private boolean isDisplayRawResultsChecked() {
    method setLoading (line 186) | private void setLoading(boolean loading) {

FILE: demo-java/src/main/java/com/example/placesdemo/FieldSelector.java
  class FieldSelector (line 44) | public final class FieldSelector {
    method allExcept (line 57) | static List<Field> allExcept(Field... placeFieldsToOmit) {
    method FieldSelector (line 65) | public FieldSelector(CheckBox enableView, TextView outputView, @Nullab...
    method FieldSelector (line 69) | public FieldSelector(
    method showDialog (line 114) | public void showDialog(Context context) {
    method getAllFields (line 134) | public List<Field> getAllFields() {
    method getSelectedFields (line 141) | public List<Field> getSelectedFields() {
    method getSelectedString (line 156) | public String getSelectedString() {
    method onSaveInstanceState (line 166) | public void onSaveInstanceState(Bundle bundle) {
    method restoreState (line 176) | private void restoreState(List<Integer> selectedFields) {
    class State (line 193) | public static final class State {
      method State (line 197) | public State(Field field) {
    class PlaceFieldArrayAdapter (line 202) | private static final class PlaceFieldArrayAdapter extends ArrayAdapter...
      method PlaceFieldArrayAdapter (line 205) | public PlaceFieldArrayAdapter(Context context, Collection<State> sta...
      method updateView (line 209) | private static void updateView(View view, State state) {
      method getView (line 217) | @Override
      method onItemClick (line 226) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/MainActivity.java
  class MainActivity (line 31) | public class MainActivity extends AppCompatActivity {
    method onCreate (line 33) | @Override
    method setLaunchActivityClickListener (line 49) | private void setLaunchActivityClickListener(

FILE: demo-java/src/main/java/com/example/placesdemo/PlaceAutocompleteActivity.java
  class PlaceAutocompleteActivity (line 64) | public class PlaceAutocompleteActivity extends AppCompatActivity {
    method onCreate (line 71) | @Override
    method onSaveInstanceState (line 107) | @Override
    method setupAutocompleteSupportFragment (line 113) | private void setupAutocompleteSupportFragment() {
    method getPlaceSelectionListener (line 136) | private PlaceSelectionListener getPlaceSelectionListener() {
    method onActivityResult (line 154) | @Override
    method startAutocompleteActivity (line 174) | private void startAutocompleteActivity() {
    method findAutocompletePredictions (line 187) | private void findAutocompletePredictions() {
    method getPlaceFields (line 223) | private List<Place.Field> getPlaceFields() {
    method getQuery (line 231) | @Nullable
    method getHint (line 236) | @Nullable
    method getCountries (line 241) | private List<String> getCountries() {
    method getTextViewValue (line 250) | @Nullable
    method getLocationBias (line 256) | @Nullable
    method getLocationRestriction (line 262) | @Nullable
    method getBounds (line 269) | @Nullable
    method getOrigin (line 286) | @Nullable
    method getTypesFilter (line 303) | private List<String> getTypesFilter() {
    method getMode (line 311) | private AutocompleteActivityMode getMode() {
    method isDisplayRawResultsChecked (line 317) | private boolean isDisplayRawResultsChecked() {
    method isUseSessionTokenChecked (line 321) | private boolean isUseSessionTokenChecked() {
    method setLoading (line 325) | private void setLoading(boolean loading) {
    method showErrorAlert (line 329) | private void showErrorAlert(@StringRes int messageResId) {

FILE: demo-java/src/main/java/com/example/placesdemo/PlaceDetailsAndPhotosActivity.java
  class PlaceDetailsAndPhotosActivity (line 57) | public class PlaceDetailsAndPhotosActivity extends AppCompatActivity {
    method onCreate (line 66) | @Override
    method onSaveInstanceState (line 103) | @Override
    method fetchPlace (line 114) | private void fetchPlace() {
    method attemptFetchPhoto (line 154) | private void attemptFetchPhoto(Place place) {
    method attemptFetchIcon (line 161) | private void attemptFetchIcon(Place place) {
    method fetchPhoto (line 174) | private void fetchPhoto(PhotoMetadata photoMetadata) {
    method dismissKeyboard (line 219) | private void dismissKeyboard(EditText focusedEditText) {
    method validateInputs (line 225) | private boolean validateInputs(boolean isFetchPhotoChecked, boolean is...
    method getPlaceId (line 247) | private String getPlaceId() {
    method getPlaceFields (line 251) | private List<Field> getPlaceFields() {
    method isDisplayRawResultsChecked (line 259) | private boolean isDisplayRawResultsChecked() {
    method isFetchPhotoChecked (line 263) | private boolean isFetchPhotoChecked() {
    method isFetchIconChecked (line 267) | private boolean isFetchIconChecked() {
    method getCustomPhotoReference (line 271) | private String getCustomPhotoReference() {
    method setPhotoSizingEnabled (line 275) | private void setPhotoSizingEnabled(boolean enabled) {
    method setCustomPhotoReferenceEnabled (line 280) | private void setCustomPhotoReferenceEnabled(boolean enabled) {
    method setEnabled (line 284) | private void setEnabled(@IdRes int resId, boolean enabled) {
    method readIntFromTextView (line 290) | @Nullable
    method showErrorAlert (line 309) | private void showErrorAlert(@StringRes int messageResId) {
    method setLoading (line 316) | private void setLoading(boolean loading) {
    method clearViews (line 320) | private void clearViews() {

FILE: demo-java/src/main/java/com/example/placesdemo/PlaceIsOpenActivity.java
  class PlaceIsOpenActivity (line 61) | public class PlaceIsOpenActivity extends AppCompatActivity {
    method onCreate (line 71) | @Override
    method onSaveInstanceState (line 105) | @Override
    method fetchPlace (line 114) | private void fetchPlace() {
    method isOpenByPlaceObject (line 142) | @SuppressLint("SetTextI18n")
    method isOpenByPlaceId (line 180) | @SuppressLint("SetTextI18n")
    method dismissKeyboard (line 215) | private void dismissKeyboard(EditText focusedEditText) {
    method getPlaceId (line 220) | private String getPlaceId() {
    method getPlaceFields (line 228) | private List<Field> getPlaceFields() {
    method setLoading (line 244) | private void setLoading(boolean loading) {
    method clearViews (line 248) | private void clearViews() {
    method initializeSpinnerAndAddListener (line 252) | private void initializeSpinnerAndAddListener() {
    method addIsOpenDateSelectionListener (line 274) | private void addIsOpenDateSelectionListener() {
    method updateIsOpenDate (line 293) | private void updateIsOpenDate() {
    method addIsOpenTimeSelectionListener (line 298) | private void addIsOpenTimeSelectionListener() {
    method updateIsOpenTime (line 316) | private void updateIsOpenTime() {

FILE: demo-java/src/main/java/com/example/placesdemo/PlacesDemoApplication.java
  class PlacesDemoApplication (line 24) | public class PlacesDemoApplication extends Application {
    method onCreate (line 25) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/StringUtil.java
  class StringUtil (line 40) | public final class StringUtil {
    method prepend (line 45) | static void prepend(TextView textView, String prefix) {
    method convertToLatLngBounds (line 49) | @Nullable
    method convertToLatLng (line 61) | @Nullable
    method countriesStringToArrayList (line 79) | static List<String> countriesStringToArrayList(String countriesString) {
    method stringify (line 87) | static String stringify(FindAutocompletePredictionsResponse response, ...
    method stringify (line 108) | static String stringify(FetchPlaceResponse response, boolean raw) {
    method stringify (line 121) | static String stringify(FindCurrentPlaceResponse response, boolean raw) {
    method stringify (line 144) | static String stringify(Place place) {
    method stringify (line 151) | static String stringify(Bitmap bitmap) {
    method stringifyAutocompleteWidget (line 164) | public static String stringifyAutocompleteWidget(Place place, boolean ...
    method appendListToStringBuilder (line 178) | private static <T> void appendListToStringBuilder(StringBuilder builde...

FILE: demo-java/src/main/java/com/example/placesdemo/model/AddressType.java
  type AddressType (line 24) | public enum AddressType {
    method AddressType (line 469) | AddressType(final String addressType) {
    method toString (line 473) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/model/AutocompleteEditText.java
  class AutocompleteEditText (line 23) | public class AutocompleteEditText extends androidx.appcompat.widget.AppC...
    method AutocompleteEditText (line 24) | public AutocompleteEditText(Context context) {
    method AutocompleteEditText (line 28) | public AutocompleteEditText(Context context, AttributeSet attrs) {
    method onTouchEvent (line 32) | @Override
    method performClick (line 49) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/model/Bounds.java
  class Bounds (line 21) | public class Bounds implements Serializable {
    method toString (line 29) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/model/GeocodingResult.java
  class GeocodingResult (line 22) | public class GeocodingResult implements Serializable {
    method toString (line 76) | @NonNull

FILE: demo-java/src/main/java/com/example/placesdemo/model/Geometry.java
  class Geometry (line 24) | public class Geometry implements Serializable {
    method toString (line 50) | @NonNull

FILE: demo-java/src/main/java/com/example/placesdemo/model/LocationType.java
  type LocationType (line 22) | public enum LocationType {

FILE: demo-java/src/main/java/com/example/placesdemo/model/PlusCode.java
  class PlusCode (line 20) | public class PlusCode implements Serializable {
    method toString (line 30) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/programmatic_autocomplete/LatLngAdapter.java
  class LatLngAdapter (line 26) | public class LatLngAdapter extends TypeAdapter<LatLng> {
    method read (line 40) | @Override
    method write (line 73) | @Override

FILE: demo-java/src/main/java/com/example/placesdemo/programmatic_autocomplete/PlacePredictionAdapter.java
  class PlacePredictionAdapter (line 32) | public class PlacePredictionAdapter extends RecyclerView.Adapter<PlacePr...
    method onCreateViewHolder (line 38) | @NonNull
    method onBindViewHolder (line 46) | @Override
    method getItemCount (line 57) | @Override
    method setPredictions (line 62) | public void setPredictions(List<AutocompletePrediction> predictions) {
    method setPlaceClickListener (line 68) | public void setPlaceClickListener(OnPlaceClickListener onPlaceClickLis...
    class PlacePredictionViewHolder (line 72) | public static class PlacePredictionViewHolder extends RecyclerView.Vie...
      method PlacePredictionViewHolder (line 77) | public PlacePredictionViewHolder(@NonNull View itemView) {
      method setPrediction (line 83) | public void setPrediction(AutocompletePrediction prediction) {
    type OnPlaceClickListener (line 89) | interface OnPlaceClickListener {
      method onPlaceClicked (line 90) | void onPlaceClicked(AutocompletePrediction place);

FILE: demo-java/src/main/java/com/example/placesdemo/programmatic_autocomplete/ProgrammaticAutocompleteToolbarActivity.java
  class ProgrammaticAutocompleteToolbarActivity (line 69) | public class ProgrammaticAutocompleteToolbarActivity extends AppCompatAc...
    method onCreate (line 84) | @Override
    method onCreateOptionsMenu (line 101) | @Override
    method onOptionsItemSelected (line 111) | @Override
    method initSearchView (line 120) | private void initSearchView(SearchView searchView) {
    method initRecyclerView (line 146) | private void initRecyclerView() {
    method getPlacePredictions (line 162) | private void getPlacePredictions(String query) {
    method geocodePlaceAndDisplay (line 203) | private void geocodePlaceAndDisplay(AutocompletePrediction placePredic...
    method displayDialog (line 233) | private void displayDialog(AutocompletePrediction place, GeocodingResu...

FILE: snippets/src/main/java/com/google/places/CurrentPlaceActivity.java
  class CurrentPlaceActivity (line 55) | public class CurrentPlaceActivity extends AppCompatActivity {
    method onCreate (line 70) | @Override
    method onOptionsItemSelected (line 92) | @Override
    method findCurrentPlace (line 101) | private void findCurrentPlace() {
    method getLocationPermission (line 137) | private void getLocationPermission() {
    class PlacesAdapter (line 142) | private static class PlacesAdapter extends RecyclerView.Adapter<Places...
      method onCreateViewHolder (line 146) | @NonNull
      method onBindViewHolder (line 153) | @Override
      method getItemCount (line 159) | @Override
      method setPlaceLikelihoods (line 164) | public void setPlaceLikelihoods(List<PlaceLikelihood> placeLikelihoo...
      class ViewHolder (line 169) | static class ViewHolder extends RecyclerView.ViewHolder {
        method ViewHolder (line 172) | ViewHolder(ListItemPlaceBinding binding) {
        method bind (line 177) | void bind(PlaceLikelihood placeLikelihood) {

FILE: snippets/src/main/java/com/google/places/GetStartedActivity.java
  class GetStartedActivity (line 26) | public class GetStartedActivity extends AppCompatActivity {
    method onCreate (line 28) | @Override

FILE: snippets/src/main/java/com/google/places/JavaMainActivity.java
  class JavaMainActivity (line 36) | public class JavaMainActivity extends AppCompatActivity {
    method onCreate (line 40) | @Override
    class ActivityInfo (line 96) | private static class ActivityInfo {
      method ActivityInfo (line 101) | ActivityInfo(String name, String description, Class<? extends AppCom...
    class ActivitiesAdapter (line 108) | private static class ActivitiesAdapter extends RecyclerView.Adapter<Ac...
      method ActivitiesAdapter (line 112) | ActivitiesAdapter(List<ActivityInfo> activities) {
      method onCreateViewHolder (line 116) | @NonNull
      method onBindViewHolder (line 127) | @Override
      method getItemCount (line 138) | @Override
      class ViewHolder (line 143) | static class ViewHolder extends RecyclerView.ViewHolder {
        method ViewHolder (line 146) | ViewHolder(ListItemActivityBinding binding) {

FILE: snippets/src/main/java/com/google/places/PlaceAutocompleteActivity.java
  class PlaceAutocompleteActivity (line 51) | public class PlaceAutocompleteActivity extends AppCompatActivity {
    method onCreate (line 57) | @Override
    method onOptionsItemSelected (line 84) | @Override
    method initAutocompleteSupportFragment (line 93) | private void initAutocompleteSupportFragment() {
    method startAutocompleteIntent (line 167) | private void startAutocompleteIntent() {
    method programmaticPlacePredictions (line 209) | private void programmaticPlacePredictions(String query) {

FILE: snippets/src/main/java/com/google/places/PlaceDetailsActivity.java
  class PlaceDetailsActivity (line 36) | public class PlaceDetailsActivity extends AppCompatActivity {
    method onCreate (line 42) | @Override
    method onOptionsItemSelected (line 62) | @Override
    method getPlaceById (line 71) | private void getPlaceById() {

FILE: snippets/src/main/java/com/google/places/PlaceIsOpenActivity.java
  class PlaceIsOpenActivity (line 41) | public class PlaceIsOpenActivity extends AppCompatActivity {
    method onCreate (line 45) | @Override
    method onOptionsItemSelected (line 68) | @Override
    method isOpenByPlaceObject (line 81) | @SuppressLint("SetTextI18n")
    method isOpenByPlaceId (line 135) | @SuppressLint("SetTextI18n")

FILE: snippets/src/main/java/com/google/places/PlacePhotosActivity.java
  class PlacePhotosActivity (line 39) | public class PlacePhotosActivity extends AppCompatActivity {
    method onCreate (line 45) | @Override
    method onOptionsItemSelected (line 65) | @Override
    method getPlacePhoto (line 74) | private void getPlacePhoto() {

FILE: snippets/src/main/java/com/google/places/PlacesIconActivity.java
  class PlacesIconActivity (line 29) | public class PlacesIconActivity extends AppCompatActivity {
    method onCreate (line 32) | @Override
    method onOptionsItemSelected (line 58) | @Override
    method getPlacesIcon (line 67) | private void getPlacesIcon(Place place) {

FILE: snippets/src/main/java/com/google/places/data/PlaceIdProvider.java
  class PlaceIdProvider (line 45) | public class PlaceIdProvider {
    method getPlaceIds (line 84) | public static List<String> getPlaceIds() {
    method getRandomPlaceId (line 104) | public static String getRandomPlaceId() {
Condensed preview — 282 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,092K chars).
[
  {
    "path": ".gemini/config.yaml",
    "chars": 1066,
    "preview": "# Copyright 2026 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".gemini/skills/places-android/SKILL.md",
    "chars": 8555,
    "preview": "---\nname: places-android\ndescription: Guide for integrating the Places SDK for Android into an application. Use when use"
  },
  {
    "path": ".gemini/styleguide.md",
    "chars": 1543,
    "preview": "# Gemini Code Assist Style Guide: android-places-demos\n\nThis guide defines the custom code review and generation rules f"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 702,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1360,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: 'type: bug, triage me'\nassignees: ''\n\n-"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 1439,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this library\ntitle: ''\nlabels: 'type: feature request, triage me'\na"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/support_request.md",
    "chars": 942,
    "preview": "---\nname: Support request\nabout: If you have a support contract with Google, please create an issue in the Google\n  Clou"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/pull_request_template.md",
    "chars": 576,
    "preview": "---\nname: Pull request\nabout: Create a pull request\nlabel: 'triage me'\n---\nThank you for opening a Pull Request! Before "
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 748,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/header-checker-lint.yml",
    "chars": 1026,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 507,
    "preview": "Thank you for opening a Pull Request!\n\n---\n\nBefore submitting your PR, there are a few things you can do to make sure it"
  },
  {
    "path": ".github/snippet-bot.yml",
    "chars": 575,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/stale.yml",
    "chars": 2614,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/sync-repo-settings.yaml",
    "chars": 1358,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 1744,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".gitignore",
    "chars": 235,
    "preview": "# Build output\nbuild/\n.gradle/\n.kotlin/\n\n# IDE files\n.idea/\n*.iml\n\n# Local machine-specific configs\n**/local.properties\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3167,
    "preview": "# Google Open Source Community Guidelines\n\nAt Google, we recognize and celebrate the creativity and collaboration of ope"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 885,
    "preview": "# How to become a contributor and submit your own code\n\n## Contributor License Agreements\n\nWe'd love to accept your samp"
  },
  {
    "path": "LICENSE",
    "chars": 11337,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "PlaceDetailsCompose/.gitignore",
    "chars": 225,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
  },
  {
    "path": "PlaceDetailsCompose/ARCHITECTURE.md",
    "chars": 3285,
    "preview": "# Architecture\n\nThis sample application demonstrates a modern **MVVM (Model-View-ViewModel)** architecture integrated wi"
  },
  {
    "path": "PlaceDetailsCompose/README.md",
    "chars": 3719,
    "preview": "# Place Details Compose Sample\n\nThis sample demonstrates how to integrate the **Places UI Kit** (specifically `PlaceDeta"
  },
  {
    "path": "PlaceDetailsCompose/build.gradle.kts",
    "chars": 6807,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "PlaceDetailsCompose/local.defaults.properties",
    "chars": 73,
    "preview": "PLACES_API_KEY=YOUR_API_KEY\nMAPS_API_KEY=YOUR_API_KEY\nMAP_ID=YOUR_MAP_ID\n"
  },
  {
    "path": "PlaceDetailsCompose/src/main/AndroidManifest.xml",
    "chars": 1931,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/MainActivity.kt",
    "chars": 5751,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/PlaceDetailsComposeApplication.kt",
    "chars": 3753,
    "preview": "/*\n* Copyright 2025 Google LLC\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use thi"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/repository/LocationRepository.kt",
    "chars": 2515,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/map/MapScreen.kt",
    "chars": 16221,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/map/PlaceContentSelectionDialog.kt",
    "chars": 3293,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/map/PlaceDetailsView.kt",
    "chars": 11200,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Color.kt",
    "chars": 882,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Theme.kt",
    "chars": 2035,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Typography.kt",
    "chars": 1136,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/ui/theme/Utils.kt",
    "chars": 2217,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "PlaceDetailsCompose/src/main/java/com/example/placedetailscompose/viewmodels/MapViewModel.kt",
    "chars": 6736,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/drawable/close_button_background.xml",
    "chars": 784,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/drawable/ic_close.xml",
    "chars": 1041,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 6178,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 2274,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/drawable/outline_my_location_24.xml",
    "chars": 1706,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/drawable/outline_settings_24.xml",
    "chars": 2285,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/layout/place_details_fragment.xml",
    "chars": 853,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/mipmap-anydpi/ic_launcher.xml",
    "chars": 915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/mipmap-anydpi/ic_launcher_round.xml",
    "chars": 915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/values/ids.xml",
    "chars": 690,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/values/strings.xml",
    "chars": 1657,
    "preview": "<!--\n     Copyright 2025 Google LLC\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may n"
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/values/styles.xml",
    "chars": 952,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/xml/backup_rules.xml",
    "chars": 1051,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsCompose/src/main/res/xml/data_extraction_rules.xml",
    "chars": 1124,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/.gitignore",
    "chars": 225,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
  },
  {
    "path": "PlaceDetailsUIKit/README.md",
    "chars": 8151,
    "preview": "# **Place Details UI Kit Samples for Android**\n\nThis Android application provides two distinct demonstrations of the **P"
  },
  {
    "path": "PlaceDetailsUIKit/build.gradle.kts",
    "chars": 6805,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "PlaceDetailsUIKit/lint.xml",
    "chars": 646,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlaceDetailsUIKit/local.defaults.properties",
    "chars": 57,
    "preview": "PLACES_API_KEY=\"YOUR_API_KEY\"\nMAPS_API_KEY=\"YOUR_API_KEY\""
  },
  {
    "path": "PlaceDetailsUIKit/proguard-rules.pro",
    "chars": 750,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "PlaceDetailsUIKit/src/androidTest/java/com/example/placedetailsuikit/MainActivityInstrumentedTest.kt",
    "chars": 6251,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/androidTest/java/com/example/placedetailsuikit/compact/ConfigurablePlaceDetailsActivityInstrumentedTest.kt",
    "chars": 6662,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/androidTest/java/com/example/placedetailsuikit/full/FullConfigurablePlaceDetailsActivityInstrumentedTest.kt",
    "chars": 6365,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/AndroidManifest.xml",
    "chars": 2320,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/LauncherActivity.kt",
    "chars": 3235,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/MainActivity.kt",
    "chars": 16144,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/compact/ConfigurablePlaceDetailsActivity.kt",
    "chars": 19249,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/compact/ContentSelectionViewModel.kt",
    "chars": 7330,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/full/FullConfigurablePlaceDetailsActivity.kt",
    "chars": 17526,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/full/FullContentSelectionViewModel.kt",
    "chars": 5641,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/ui/theme/Color.kt",
    "chars": 879,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/ui/theme/Theme.kt",
    "chars": 2794,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/java/com/example/placedetailsuikit/ui/theme/Type.kt",
    "chars": 1584,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/drawable/close_button_background.xml",
    "chars": 784,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/drawable/ic_close.xml",
    "chars": 1041,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 6178,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 2274,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/drawable/outline_my_location_24.xml",
    "chars": 1706,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/drawable/outline_settings_24.xml",
    "chars": 2285,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/font/custom_font.xml",
    "chars": 1159,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/layout/activity_configurable_map.xml",
    "chars": 4702,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/layout/activity_full_configurable_map.xml",
    "chars": 4728,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/layout/activity_main.xml",
    "chars": 3394,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/layout/content_selector_dialog.xml",
    "chars": 992,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/mipmap-anydpi/ic_launcher.xml",
    "chars": 915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/mipmap-anydpi/ic_launcher_round.xml",
    "chars": 915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/values/colors.xml",
    "chars": 1363,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/values/strings.xml",
    "chars": 951,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/values/themes.xml",
    "chars": 2650,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/values-night/themes.xml",
    "chars": 909,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/xml/backup_rules.xml",
    "chars": 1051,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/main/res/xml/data_extraction_rules.xml",
    "chars": 1124,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlaceDetailsUIKit/src/test/java/com/example/placedetailsuikit/ExampleUnitTest.kt",
    "chars": 938,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/.gitignore",
    "chars": 245,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
  },
  {
    "path": "PlacesUIKit3D/README.md",
    "chars": 2830,
    "preview": "# Places UI Kit 3D Sample\n\nThis sample demonstrates how to integrate the Places SDK for Android's `PlaceDetailsCompactFr"
  },
  {
    "path": "PlacesUIKit3D/build.gradle.kts",
    "chars": 7229,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "PlacesUIKit3D/local.defaults.properties",
    "chars": 61,
    "preview": "MAPS3D_API_KEY=DEFAULT_API_KEY\nPLACES_API_KEY=DEFAULT_API_KEY"
  },
  {
    "path": "PlacesUIKit3D/proguard-rules.pro",
    "chars": 750,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "PlacesUIKit3D/src/main/AndroidManifest.xml",
    "chars": 2044,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/Landmark.kt",
    "chars": 1039,
    "preview": "// Copyright 2026 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/LandmarkList.kt",
    "chars": 3213,
    "preview": "// Copyright 2026 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/MainActivity.kt",
    "chars": 16068,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/MainViewModel.kt",
    "chars": 3695,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/Maps3DPlacesApplication.kt",
    "chars": 3383,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/common/ActiveMapObject.kt",
    "chars": 1420,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/common/Map3dViewModel.kt",
    "chars": 12764,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/common/MapObject.kt",
    "chars": 2175,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/ui/theme/Color.kt",
    "chars": 874,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/ui/theme/Theme.kt",
    "chars": 2263,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/ui/theme/Type.kt",
    "chars": 1579,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/utils/CameraUpdate.kt",
    "chars": 4287,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/utils/Units.kt",
    "chars": 6141,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/java/com/example/placesuikit3d/utils/Utilities.kt",
    "chars": 12507,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/close_button_background.xml",
    "chars": 783,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/ic_close.xml",
    "chars": 1040,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 6178,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 2313,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/ic_my_location.xml",
    "chars": 1226,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/loader_background.xml",
    "chars": 826,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/drawable/outline_my_location_24.xml",
    "chars": 1706,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/font/custom_font.xml",
    "chars": 1158,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/layout/activity_main.xml",
    "chars": 4726,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/mipmap-anydpi/ic_launcher.xml",
    "chars": 915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/mipmap-anydpi/ic_launcher_round.xml",
    "chars": 915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/values/colors.xml",
    "chars": 1677,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/values/strings.xml",
    "chars": 1704,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/values/themes.xml",
    "chars": 2637,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/xml/backup_rules.xml",
    "chars": 1050,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "PlacesUIKit3D/src/main/res/xml/data_extraction_rules.xml",
    "chars": 1123,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "README.md",
    "chars": 6799,
    "preview": "Google Places SDK for Android Demos\n====================================\n![GitHub contributors](https://img.shields.io/g"
  },
  {
    "path": "SECURITY.md",
    "chars": 475,
    "preview": "# Report a security issue\n\nTo report a security issue, please use https://g.co/vulnz. We use\nhttps://g.co/vulnz for our "
  },
  {
    "path": "build-logic/convention/build.gradle.kts",
    "chars": 803,
    "preview": "/*\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/places-demo.android.application.gradle.kts",
    "chars": 1990,
    "preview": "/*\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/places-demo.secrets.gradle.kts",
    "chars": 6642,
    "preview": "/*\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "build-logic/settings.gradle.kts",
    "chars": 1192,
    "preview": "/*\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "build.gradle.kts",
    "chars": 1279,
    "preview": "/*\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/build.gradle.kts",
    "chars": 1541,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/local.defaults.properties",
    "chars": 60,
    "preview": "PLACES_API_KEY=DEFAULT_API_KEY\nMAPS_API_KEY=DEFAULT_API_KEY\n"
  },
  {
    "path": "demo-java/proguard-rules.pro",
    "chars": 755,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "demo-java/src/main/AndroidManifest.xml",
    "chars": 2739,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 ("
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/AutocompleteAddressActivity.java",
    "chars": 16465,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/CurrentPlaceActivity.java",
    "chars": 7279,
    "preview": "/*\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/FieldSelector.java",
    "chars": 6997,
    "preview": "/*\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/MainActivity.java",
    "chars": 2415,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/PlaceAutocompleteActivity.java",
    "chars": 13320,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/PlaceDetailsAndPhotosActivity.java",
    "chars": 11989,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/PlaceIsOpenActivity.java",
    "chars": 12185,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/PlacesDemoApplication.java",
    "chars": 1159,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/StringUtil.java",
    "chars": 5703,
    "preview": "/*\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/AddressType.java",
    "chars": 13520,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/AutocompleteEditText.java",
    "chars": 1639,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/Bounds.java",
    "chars": 1153,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/GeocodingResult.java",
    "chars": 3932,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/Geometry.java",
    "chars": 1870,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/LocationType.java",
    "chars": 1740,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/model/PlusCode.java",
    "chars": 1267,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/programmatic_autocomplete/LatLngAdapter.java",
    "chars": 2421,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/programmatic_autocomplete/PlacePredictionAdapter.java",
    "chars": 3396,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/java/com/example/placesdemo/programmatic_autocomplete/ProgrammaticAutocompleteToolbarActivity.java",
    "chars": 11034,
    "preview": "// Copyright 2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-java/src/main/res/drawable/ic_search_black_24dp.xml",
    "chars": 1110,
    "preview": "<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-java/src/main/res/layout/activity_main.xml",
    "chars": 3123,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/activity_programmatic_autocomplete.xml",
    "chars": 2499,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/autocomplete_address_activity.xml",
    "chars": 6498,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2021 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/autocomplete_address_map.xml",
    "chars": 1482,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2021 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/current_place_activity.xml",
    "chars": 2850,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/place_autocomplete_activity.xml",
    "chars": 9403,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/place_details_and_photos_activity.xml",
    "chars": 6625,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/place_is_open_activity.xml",
    "chars": 5932,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/layout/place_prediction_item.xml",
    "chars": 1361,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-java/src/main/res/menu/menu.xml",
    "chars": 926,
    "preview": "<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-java/src/main/res/raw/style_json.json",
    "chars": 270,
    "preview": "[\n  {\n    \"featureType\": \"poi\",\n    \"elementType\": \"all\",\n    \"stylers\": [\n      {\n        \"visibility\": \"off\"\n      }\n "
  },
  {
    "path": "demo-java/src/main/res/values/dimens.xml",
    "chars": 723,
    "preview": "<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-java/src/main/res/values/strings.xml",
    "chars": 12621,
    "preview": "<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-kotlin/build.gradle.kts",
    "chars": 1739,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/local.defaults.properties",
    "chars": 57,
    "preview": "PLACES_API_KEY=\"YOUR_API_KEY\"\nMAPS_API_KEY=\"YOUR_API_KEY\""
  },
  {
    "path": "demo-kotlin/src/main/AndroidManifest.xml",
    "chars": 2658,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/AutocompleteAddressActivity.kt",
    "chars": 15878,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/BaseActivity.kt",
    "chars": 2666,
    "preview": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/CurrentPlaceActivity.kt",
    "chars": 7363,
    "preview": "/*\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/FieldSelector.kt",
    "chars": 6623,
    "preview": "/*\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/MainActivity.kt",
    "chars": 2375,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/PlaceAutocompleteActivity.kt",
    "chars": 11902,
    "preview": "/*\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/PlaceDetailsAndPhotosActivity.kt",
    "chars": 11002,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/PlaceIsOpenActivity.kt",
    "chars": 11185,
    "preview": "/*\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/PlacesDemoApplication.kt",
    "chars": 1143,
    "preview": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/PlacesDemoGlideModule.kt",
    "chars": 784,
    "preview": "/*\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/StringUtil.kt",
    "chars": 6832,
    "preview": "/*\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/AddressType.kt",
    "chars": 14112,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/AutocompleteEditText.kt",
    "chars": 1564,
    "preview": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/Bounds.kt",
    "chars": 1182,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/GeocodingResult.kt",
    "chars": 3883,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/Geometry.kt",
    "chars": 1893,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/LocationType.kt",
    "chars": 1770,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/model/PlusCode.kt",
    "chars": 1304,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/programmatic_autocomplete/LatLngAdapter.kt",
    "chars": 2340,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/programmatic_autocomplete/PlacePredictionAdapter.kt",
    "chars": 2783,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/java/com/example/placesdemo/programmatic_autocomplete/ProgrammaticAutocompleteGeocodingActivity.kt",
    "chars": 12968,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "demo-kotlin/src/main/res/drawable/ic_exit.xml",
    "chars": 993,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-kotlin/src/main/res/drawable/ic_exit_to_app_black_24dp.xml",
    "chars": 1083,
    "preview": "<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-kotlin/src/main/res/drawable/ic_search_black_24dp.xml",
    "chars": 1165,
    "preview": "<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this "
  },
  {
    "path": "demo-kotlin/src/main/res/layout/activity_main.xml",
    "chars": 3919,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-kotlin/src/main/res/layout/activity_programmatic_autocomplete.xml",
    "chars": 3602,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-kotlin/src/main/res/layout/autocomplete_address_activity.xml",
    "chars": 6967,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2025 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "demo-kotlin/src/main/res/layout/autocomplete_address_map.xml",
    "chars": 1482,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright 2021 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  }
]

// ... and 82 more files (download for full content)

About this extraction

This page contains the full source code of the googlemaps/android-places-demos GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 282 files (1005.3 KB), approximately 230.0k tokens, and a symbol index with 207 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!