Skip to content

Commit 5075de1

Browse files
authored
docs(places): add Gemini skill for Places SDK and enforce style guide (#1059)
Added setup guidelines for programmatic initialization, Secrets plugin enforcement, Compose interop for View-based fragments, and autocomplete widget integration.
1 parent f66708b commit 5075de1

3 files changed

Lines changed: 272 additions & 0 deletions

File tree

.gemini/config.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Gemini Code Assist Configuration
16+
# See: https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github
17+
18+
# Feature settings
19+
have_fun: false
20+
21+
code_review:
22+
disable: false
23+
comment_severity_threshold: MEDIUM
24+
max_review_comments: -1
25+
26+
pull_request_opened:
27+
summary: true
28+
code_review: true
29+
include_drafts: true
30+
31+
# Files to ignore in Gemini analysis
32+
ignore_patterns:
33+
- "**/*.bin"
34+
- "**/*.exe"
35+
- "**/build/**"
36+
- "**/.gradle/**"
37+
- "**/secrets.properties"
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
name: places-android
3+
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.
4+
---
5+
6+
# Places SDK for Android Integration
7+
8+
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:
9+
10+
### 📋 Design & Architectural Questions to Ask the User
11+
12+
* **UI Framework**: Are you using Jetpack Compose or standard UI Views?
13+
* **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?
14+
* **Compact vs Full Details**: Do you want a compact half-sheet overlay (`PlaceDetailsCompactFragment`) or a full-page details viewer (`PlaceDetailsFragment`)?
15+
* **Cost & Field Scoping**: What exact fields do you need (e.g., `DISPLAY_NAME`, `FORMATTED_ADDRESS`, `PHOTO_METADATAS`)? Limiting fields saves costs!
16+
* **Theming Options**: Are you using Material 3 themes so we can bridge default styling automatically?
17+
18+
---
19+
20+
## 1. Setup Dependencies
21+
22+
Add the necessary dependencies to your module-level `build.gradle.kts` file. It is recommended to use the Versions Catalog if available:
23+
24+
```toml
25+
[versions]
26+
places = "5.1.1" # x-release-please-version
27+
28+
[libraries]
29+
places = { group = "com.google.android.libraries.places", name = "places", version.ref = "places" }
30+
```
31+
32+
Then in `build.gradle.kts`:
33+
34+
```kotlin
35+
dependencies {
36+
implementation(libs.places)
37+
}
38+
```
39+
40+
## 2. Setup the Secrets Gradle Plugin
41+
42+
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.
43+
44+
Ensure you have the plugin applied in your app-level `build.gradle.kts`:
45+
46+
```kotlin
47+
plugins {
48+
alias(libs.plugins.secrets.gradle.plugin)
49+
}
50+
51+
secrets {
52+
propertiesFileName = "secrets.properties"
53+
defaultPropertiesFileName = "local.defaults.properties"
54+
}
55+
```
56+
57+
Add your API Key to `secrets.properties`:
58+
59+
```properties
60+
PLACES_API_KEY=YOUR_API_KEY
61+
```
62+
63+
## 3. Initialize the Places SDK
64+
65+
In your `Application` or `Activity` (before accessing any Places APIs, usually inside `onCreate`), initialize the Places SDK.
66+
67+
### Kotlin
68+
69+
```kotlin
70+
import com.google.android.libraries.places.api.Places
71+
72+
class DemoApplication : Application() {
73+
override fun onCreate() {
74+
super.onCreate()
75+
76+
val apiKey = BuildConfig.PLACES_API_KEY
77+
if (apiKey.isNotEmpty()) {
78+
Places.initializeWithNewPlacesApiEnabled(applicationContext, apiKey)
79+
}
80+
}
81+
}
82+
```
83+
84+
### Java
85+
86+
```java
87+
import com.google.android.libraries.places.api.Places;
88+
89+
public class DemoApplication extends Application {
90+
@Override
91+
public void onCreate() {
92+
super.onCreate();
93+
94+
String apiKey = BuildConfig.PLACES_API_KEY;
95+
if (!apiKey.isEmpty()) {
96+
Places.initializeWithNewPlacesApiEnabled(getApplicationContext(), apiKey);
97+
}
98+
}
99+
}
100+
```
101+
102+
## 4. Best Practices & Guidelines
103+
104+
* **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!
105+
* **Null Safety & Validation**: Handle nulls defensively for optional parameters (e.g. Place fields).
106+
* **Scoped Fields**: Always specify *only* parameters that are needed (e.g. `Place.Field.ID`, `Place.Field.DISPLAY_NAME`) to avoid over-billing.
107+
* **Coroutine Extensions**: Use Kotlin Coroutines extensions (`places-ktx` if available) to make code cleaner.
108+
* **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`:
109+
110+
```xml
111+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
112+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
113+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
114+
</manifest>
115+
```
116+
117+
## 5. Compose Interop with Places UI Kit
118+
119+
The Places UI Kit (`PlaceDetailsCompactFragment` and `PlaceDetailsFragment`) are View-based. To use them in Jetpack Compose, use `AndroidView` to host a `FragmentContainerView`.
120+
121+
### Key Pattern: Fragment Container in Compose
122+
123+
* **Access FragmentManager**: Use standard `LocalActivity.current as FragmentActivity` to access the support FragmentManager. Avoid casting `LocalContext.current` directly to Activity.
124+
* **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.
125+
126+
```kotlin
127+
@Composable
128+
fun PlaceDetailsCompactView(
129+
placeId: String,
130+
onDismiss: () -> Unit
131+
) {
132+
val fragmentContainerId = remember { View.generateViewId() }
133+
val fragmentManager = (LocalActivity.current as FragmentActivity).supportFragmentManager
134+
135+
val fragment = remember {
136+
PlaceDetailsCompactFragment.newInstance(
137+
PlaceDetailsCompactFragment.ALL_CONTENT,
138+
Orientation.VERTICAL
139+
)
140+
}
141+
142+
Box(modifier = Modifier.fillMaxWidth()) {
143+
AndroidView(
144+
modifier = Modifier.fillMaxWidth(),
145+
factory = { context ->
146+
FragmentContainerView(context).apply {
147+
id = fragmentContainerId
148+
if (fragmentManager.findFragmentById(fragmentContainerId) == null) {
149+
fragmentManager.beginTransaction()
150+
.add(fragmentContainerId, fragment)
151+
.commit()
152+
}
153+
}
154+
},
155+
update = { view ->
156+
// Ensures updates run after view hierarchy is ready
157+
view.post {
158+
fragment.loadWithPlaceId(placeId)
159+
}
160+
}
161+
)
162+
}
163+
}
164+
```
165+
166+
## 📏 6. Advanced Compose Viewports & BottomSheetScaffold
167+
168+
When hosting UI Kit fragments inside navigation drawers or overlays, follow these architectural bounds to avoid viewport clipping snags:
169+
170+
* **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:
171+
```kotlin
172+
Column(modifier = modifier.statusBarsPadding()) {
173+
// Beautiful search results sit safely under the status bar
174+
}
175+
```
176+
* **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:
177+
```kotlin
178+
enum class DetailsUiType { COMPACT, FULL }
179+
```
180+
Track `currentUiType` at the Activity level and pass it to a single shared `BottomSheetScaffold`. Users can swipe to dismiss natively without custom button overrides!
181+
182+
## 7. Autocomplete with Compose (Widget)
183+
184+
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.
185+
186+
```kotlin
187+
@Composable
188+
fun AutocompleteSearchButton() {
189+
val context = LocalContext.current
190+
191+
val fields = listOf(Place.Field.ID, Place.Field.DISPLAY_NAME, Place.Field.FORMATTED_ADDRESS)
192+
val intent = Autocomplete.IntentBuilder(AutocompleteActivityMode.OVERLAY, fields)
193+
.build(context)
194+
195+
val launcher = rememberLauncherForActivityResult(
196+
contract = ActivityResultContracts.StartActivityForResult()
197+
) { result ->
198+
if (result.resultCode == Activity.RESULT_OK) {
199+
val place = Autocomplete.getPlaceFromIntent(result.data!!)
200+
Log.d("Autocomplete", "Place selected: ${place.name}")
201+
}
202+
}
203+
204+
Button(onClick = { launcher.launch(intent) }) {
205+
Text("Search Places")
206+
}
207+
}
208+
```
209+
210+
## 8. Execution Steps
211+
212+
1. Add the Places SDK dependencies to `build.gradle.kts`.
213+
2. Set up the Secrets Gradle Plugin in `build.gradle.kts`.
214+
3. Implement initialization (e.g., in a subclass of `Application`).
215+
4. Provide a summary of how to use it (retrieve place details, display autocomplete).

.gemini/styleguide.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Gemini Code Assist Style Guide: android-places-demos
2+
3+
This guide defines the custom code review and generation rules for the `android-places-demos` project.
4+
5+
## Jetpack Compose Guidelines
6+
- **API Guidelines**: Strictly follow the [Jetpack Compose API guidelines](https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md).
7+
- **Naming**: Composable functions must be PascalCase.
8+
- **Modifiers**: The first optional parameter of any Composable should be `modifier: Modifier = Modifier`.
9+
10+
## Kotlin & Java Style
11+
- **Naming**: Use camelCase for variables and functions.
12+
- **Documentation**: Provide KDoc for all public classes, properties, and functions. Explain the "why" in comments, not just the "what".
13+
- **Safety**: Use null-safe operators and avoid `!!` in Kotlin. In Java, use standard null checks or `@NonNull`/`@Nullable` annotations if available.
14+
- **Imports vs FQCNs**: Avoid using Fully Qualified Class Names (FQCNs) in code if a standard `import` statement would suffice. Keep code readable.
15+
16+
## Places SDK Specifics
17+
- **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.
18+
- **Initialization**:
19+
- Strongly recommend `Places.initializeWithNewPlacesApiEnabled` over the legacy `Places.initialize` for modern demos.
20+
- **Literate Programming**: Write clear, well-documented code that functions as an example for developers. Explain architectural decisions.

0 commit comments

Comments
 (0)