Skip to content

Commit f005d29

Browse files
authored
chore: Implement edge-to-edge UI (#1574)
* Rename .java to .kt * refactor(library): Convert projection.Point to Kotlin and add tests The `@Deprecated` annotation has been updated to the Kotlin syntax and now includes a `ReplaceWith` expression to provide IDE quick-fix support. Additionally, new unit tests using Google Truth have been added for the `Point` class to verify its construction and inheritance. * refactor(heatmaps): Port Gradient and WeightedLatLng to Kotlin This commit migrates the `Gradient` and `WeightedLatLng` classes and their corresponding tests from Java to idiomatic Kotlin. This conversion is part of the larger effort to modernize the library and prepare it for Kotlin Multiplatform (KMP) compatibility. Key changes include: - `WeightedLatLng` is now a `data class` with a private primary constructor to ensure correct instantiation while providing the benefits of auto-generated methods. The `@ExposedCopyVisibility` annotation has been added to maintain binary compatibility for the `copy()` method. - `Gradient` has been converted to a Kotlin class, using `@JvmOverloads` for constructor compatibility and a `companion object` for its static helper method. - The `internal` visibility of a method in `Gradient` was changed to `public` to ensure visibility to Java classes during the mixed-language phase of the migration. - Unit tests for both classes have been ported to Kotlin, using the Truth assertion framework. * feat(heatmaps): Port HeatmapTileProvider to Kotlin This commit completes the migration of the `heatmaps` package to Kotlin by porting the final and most complex class, `HeatmapTileProvider`. This conversion modernizes the class by introducing several idiomatic Kotlin features and improves its design by refactoring a key data structure. Key changes include: - The `HeatmapTileProvider` and its `Builder` are now written in Kotlin. - The `getMaxValue()` method was refactored to use a `Map` with a type-safe `Vector` key instead of the Android-specific `LongSparseArray`, improving readability and moving closer to KMP compatibility. - A new test suite was created from scratch for the provider, as none existed previously. This improves the code coverage and reliability of the module. - Necessary adjustments were made to the demo application to support the newly ported Kotlin class. * fix(tests): Correct copyright headers in new test files Updates the copyright headers in the test files that were created during the Kotlin porting process. - Corrects the copyright year to 2025. - Corrects the company name from "Google Inc." to "Google LLC". * chore: Add PLACES_API_KEY to local.defaults.properties * fix(heatmaps): Correct copyright headers Updates the copyright headers in the `heatmaps` package source and test files. - Corrects the copyright year to 2025. - Corrects the company name from "Google Inc." to "Google LLC". * feat: Implement edge-to-edge UI This commit implements edge-to-edge UI for the demo application. Changes include: - Updating the app theme to be edge-to-edge. - Applying window insets to the base and main activities. - Adding a separate styles.xml for API 27+ to handle display cutouts. * fix: Update copyright headers This commit updates the copyright headers in the following files: - demo/build.gradle.kts - demo/src/main/java/com/google/maps/android/utils/demo/BaseDemoActivity.java - demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.java - demo/src/main/res/values/styles.xml - demo/src/main/res/values-v27/styles.xml * chore: fix copyright headers * Fix copyright year * Fix copyright header in main.xml * refactor(heatmaps): Replace check with require in HeatmapTileProvider.Builder This commit replaces the `check` function with `require` for validating input data in the `HeatmapTileProvider.Builder`. Using `require` is more idiomatic for precondition checks, throwing an `IllegalArgumentException` which is more appropriate in this context than the `IllegalStateException` thrown by `check`. * chore(heatmaps): Correct expected exception in HeatmapTileProviderTest This commit updates `HeatmapTileProviderTest` to expect an `IllegalArgumentException` instead of `IllegalStateException` when building a provider with no data. It also cleans up unused exception variables in catch blocks.
1 parent 5d48344 commit f005d29

File tree

9 files changed

+153
-44
lines changed

9 files changed

+153
-44
lines changed

demo/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2024 Google Inc.
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -65,6 +65,7 @@ dependencies {
6565
implementation(libs.kotlinx.coroutines.android)
6666
implementation(libs.kotlinx.coroutines.core)
6767
implementation(libs.material)
68+
implementation(libs.core.ktx)
6869

6970
testImplementation(libs.junit)
7071
testImplementation(libs.truth)

demo/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8" standalone="no"?><!--
2-
Copyright 2024 Google LLC
2+
Copyright 2025 Google LLC
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.

demo/src/main/java/com/google/maps/android/utils/demo/BaseDemoActivity.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013 Google Inc.
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import static com.google.maps.android.utils.demo.ApiKeyValidatorKt.hasMapsApiKey;
2020

2121
import android.os.Bundle;
22+
import android.view.View;
2223
import android.widget.Toast;
2324

2425
import androidx.annotation.NonNull;
@@ -30,6 +31,11 @@
3031

3132
import java.util.Objects;
3233

34+
import androidx.core.graphics.Insets;
35+
import androidx.core.view.ViewCompat;
36+
import androidx.core.view.WindowCompat;
37+
import androidx.core.view.WindowInsetsCompat;
38+
3339
public abstract class BaseDemoActivity extends FragmentActivity implements OnMapReadyCallback {
3440
private GoogleMap mMap;
3541
private boolean mIsRestore;
@@ -49,6 +55,30 @@ public void onCreate(Bundle savedInstanceState) {
4955

5056
mIsRestore = savedInstanceState != null;
5157
setContentView(getLayoutId());
58+
// This tells the system that the app will handle drawing behind the system bars.
59+
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
60+
61+
// This is the root view of my layout.
62+
// Make sure to replace R.id.root_layout with the actual ID of your root view.
63+
final View rootView = findViewById(android.R.id.content);
64+
65+
// Add a listener to handle window insets.
66+
ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
67+
final Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
68+
69+
// Apply the insets as padding to the view.
70+
// This will push the content down from behind the status bar and up from
71+
// behind the navigation bar.
72+
view.setPadding(
73+
insets.left,
74+
insets.top,
75+
insets.right,
76+
insets.bottom
77+
);
78+
79+
// Return CONSUMED to signal that we've handled the insets.
80+
return WindowInsetsCompat.CONSUMED;
81+
});
5282
setUpMap();
5383
}
5484

@@ -73,4 +103,4 @@ private void setUpMap() {
73103
protected GoogleMap getMap() {
74104
return mMap;
75105
}
76-
}
106+
}

demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 Google Inc.
2+
* Copyright 2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,11 @@
2525

2626
import androidx.appcompat.app.AppCompatActivity;
2727

28+
import androidx.core.graphics.Insets;
29+
import androidx.core.view.ViewCompat;
30+
import androidx.core.view.WindowCompat;
31+
import androidx.core.view.WindowInsetsCompat;
32+
2833
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
2934
private ViewGroup mListView;
3035

@@ -34,6 +39,31 @@ protected void onCreate(Bundle savedInstanceState) {
3439

3540
setContentView(R.layout.main);
3641

42+
// This tells the system that the app will handle drawing behind the system bars.
43+
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
44+
45+
// This is the root view of my layout.
46+
// Make sure to replace R.id.root_layout with the actual ID of your root view.
47+
final View rootView = findViewById(android.R.id.content);
48+
49+
// Add a listener to handle window insets.
50+
ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
51+
final Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
52+
53+
// Apply the insets as padding to the view.
54+
// This will push the content down from behind the status bar and up from
55+
// behind the navigation bar.
56+
view.setPadding(
57+
insets.left,
58+
insets.top,
59+
insets.right,
60+
insets.bottom
61+
);
62+
63+
// Return CONSUMED to signal that we've handled the insets.
64+
return WindowInsetsCompat.CONSUMED;
65+
});
66+
3767
mListView = findViewById(R.id.list);
3868

3969
addDemo("Advanced Markers Clustering Example", CustomAdvancedMarkerClusteringDemoActivity.class);

demo/src/main/res/layout/main.xml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<!--
3-
~ Copyright 2020 Google Inc.
4-
~
5-
~ Licensed under the Apache License, Version 2.0 (the "License");
6-
~ you may not use this file except in compliance with the License.
7-
~ You may obtain a copy of the License at
8-
~
9-
~ http://www.apache.org/licenses/LICENSE-2.0
10-
~
11-
~ Unless required by applicable law or agreed to in writing, software
12-
~ distributed under the License is distributed on an "AS IS" BASIS,
13-
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
~ See the License for the specific language governing permissions and
15-
~ limitations under the License.
3+
Copyright 2025 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
1616
-->
1717

1818
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!--
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
<resources>
18+
19+
<!-- Base application theme. -->
20+
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
21+
<!-- Other theme attributes -->
22+
23+
<!-- Make status and navigation bars transparent -->
24+
<item name="android:statusBarColor">@android:color/transparent</item>
25+
<item name="android:navigationBarColor">@android:color/transparent</item>
26+
27+
<!--
28+
Required for edge-to-edge display. This allows the app to draw behind
29+
the system bars.
30+
-->
31+
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
32+
33+
<!--
34+
On API 27+, this allows the app to render in the display cutout area.
35+
'shortEdges' is a good default.
36+
-->
37+
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
38+
</style>
39+
40+
</resources>
Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
<!--
2-
~ Copyright 2020 Google Inc.
3-
~
4-
~ Licensed under the Apache License, Version 2.0 (the "License");
5-
~ you may not use this file except in compliance with the License.
6-
~ You may obtain a copy of the License at
7-
~
8-
~ http://www.apache.org/licenses/LICENSE-2.0
9-
~
10-
~ Unless required by applicable law or agreed to in writing, software
11-
~ distributed under the License is distributed on an "AS IS" BASIS,
12-
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
~ See the License for the specific language governing permissions and
14-
~ limitations under the License.
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
1515
-->
16+
1617
<resources>
1718

1819
<!-- Base application theme. -->
19-
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
20-
<!-- Customize your theme here. -->
21-
<item name="colorPrimary">@color/colorPrimary</item>
22-
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
23-
<item name="colorAccent">@color/colorAccent</item>
20+
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
21+
<!-- Other theme attributes -->
22+
23+
<!-- Make status and navigation bars transparent -->
24+
<item name="android:statusBarColor">@android:color/transparent</item>
25+
<item name="android:navigationBarColor">@android:color/transparent</item>
26+
27+
<!--
28+
Required for edge-to-edge display. This allows the app to draw behind
29+
the system bars.
30+
-->
31+
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
2432
</style>
2533

2634
</resources>

library/src/main/java/com/google/maps/android/heatmaps/HeatmapTileProvider.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class HeatmapTileProvider private constructor(builder: Builder) : TileProvider {
138138
* @return A new HeatmapTileProvider.
139139
*/
140140
fun build(): HeatmapTileProvider {
141-
check(this.weightedData?.isNotEmpty() == true) { "No input data: you must use either .data or .weightedData before building." }
141+
require(this.weightedData?.isNotEmpty() == true) { "No input data: you must use either .data or .weightedData before building." }
142142
return HeatmapTileProvider(this)
143143
}
144144
}
@@ -423,4 +423,4 @@ class HeatmapTileProvider private constructor(builder: Builder) : TileProvider {
423423
return buckets.values.maxOrNull() ?: 0.0
424424
}
425425
}
426-
}
426+
}

library/src/test/java/com/google/maps/android/heatmaps/HeatmapTileProviderTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class HeatmapTileProviderTest {
4646
try {
4747
HeatmapTileProvider.Builder().build()
4848
fail("Should have thrown IllegalStateException")
49-
} catch (e: IllegalStateException) {
49+
} catch (_: IllegalArgumentException) {
5050
// success
5151
}
5252
}
@@ -56,7 +56,7 @@ class HeatmapTileProviderTest {
5656
try {
5757
HeatmapTileProvider.Builder().data(emptyList())
5858
fail("Should have thrown IllegalArgumentException")
59-
} catch (e: IllegalArgumentException) {
59+
} catch (_: IllegalArgumentException) {
6060
// success
6161
}
6262
}
@@ -74,13 +74,13 @@ class HeatmapTileProviderTest {
7474
try {
7575
HeatmapTileProvider.Builder().data(data).radius(0)
7676
fail("Should have thrown IllegalArgumentException")
77-
} catch (e: IllegalArgumentException) {
77+
} catch (_: IllegalArgumentException) {
7878
// success
7979
}
8080
try {
8181
HeatmapTileProvider.Builder().data(data).radius(100)
8282
fail("Should have thrown IllegalArgumentException")
83-
} catch (e: IllegalArgumentException) {
83+
} catch (_: IllegalArgumentException) {
8484
// success
8585
}
8686
}
@@ -98,13 +98,13 @@ class HeatmapTileProviderTest {
9898
try {
9999
HeatmapTileProvider.Builder().data(data).opacity(-1.0)
100100
fail("Should have thrown IllegalArgumentException")
101-
} catch (e: IllegalArgumentException) {
101+
} catch (_: IllegalArgumentException) {
102102
// success
103103
}
104104
try {
105105
HeatmapTileProvider.Builder().data(data).opacity(2.0)
106106
fail("Should have thrown IllegalArgumentException")
107-
} catch (e: IllegalArgumentException) {
107+
} catch (_: IllegalArgumentException) {
108108
// success
109109
}
110110
}

0 commit comments

Comments
 (0)