Skip to content

Commit

Permalink
feat: Try to catch invalid map api keys in the demo (#1400)
Browse files Browse the repository at this point in the history
* feat: Try to catch invalid map api keys in the demo
feat: Export the demo activities so they can be run directly
chore: Update gradle version, Google maps to 19.0.0

* fix: Add copyright header.  Add testing

* fix: Copyright header error

* fix: Copyright header error for strings.xml

* fix: Correct copyright attribution

* fix: class name typo
chore: remove unused robolectric dependency
  • Loading branch information
dkhawk authored Oct 8, 2024
1 parent dd85f6f commit fc7bada
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 51 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

buildscript {
ext.kotlin_version = '1.9.24'
ext.kotlin_version = '2.0.0'
repositories {
google()
mavenCentral()
Expand All @@ -24,7 +24,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.4.0'
classpath 'com.android.tools.build:gradle:8.7.0'
classpath 'com.mxalbert.gradle:jacoco-android:0.2.1'
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
Expand Down
7 changes: 4 additions & 3 deletions demo/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,16 @@ dependencies {
// [START_EXCLUDE silent]
implementation project(':library')

implementation 'androidx.appcompat:appcompat:1.7.0-beta01'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.6"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1'

testImplementation("junit:junit:4.13.2")
testImplementation("com.google.truth:truth:1.4.2")
// [END_EXCLUDE]
}
// [END maps_android_utils_install_snippet]
Expand Down
85 changes: 60 additions & 25 deletions demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8" standalone="no"?><!--
Copyright 2024 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -14,7 +13,6 @@
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">

Expand Down Expand Up @@ -58,35 +56,72 @@
android:value="${MAPS_API_KEY}" />

<activity
android:exported="true"
android:name=".MainActivity"
android:label="@string/app_name">
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=".PolyDecodeDemoActivity" />
<activity android:name=".PolySimplifyDemoActivity" />
<activity android:name=".IconGeneratorDemoActivity" />
<activity android:name=".DistanceDemoActivity" />
<activity android:name=".ClusteringDemoActivity" />
<activity android:name=".BigClusteringDemoActivity" />
<activity android:name=".VisibleClusteringDemoActivity" />
<activity android:name=".CustomMarkerClusteringDemoActivity" />
<activity android:name=".CustomAdvancedMarkerClusteringDemoActivity" />
<activity android:name=".ZoomClusteringDemoActivity" />
<activity android:name=".ClusteringViewModelDemoActivity"/>
<activity android:name=".TileProviderAndProjectionDemo" />
<activity android:name=".HeatmapsDemoActivity" />
<activity android:name=".HeatmapsPlacesDemoActivity" />
<activity android:name=".GeoJsonDemoActivity" />
<activity android:name=".KmlDemoActivity" />
<activity android:name=".MultiLayerDemoActivity" />
<activity android:name=".AnimationUtilDemoActivity" />
<activity android:name=".StreetViewDemoActivity" />
<activity
android:name=".PolyDecodeDemoActivity"
android:exported="true" />
<activity
android:name=".PolySimplifyDemoActivity"
android:exported="true" />
<activity
android:name=".IconGeneratorDemoActivity"
android:exported="true" />
<activity
android:name=".DistanceDemoActivity"
android:exported="true" />
<activity
android:name=".ClusteringDemoActivity"
android:exported="true" />
<activity
android:name=".BigClusteringDemoActivity"
android:exported="true" />
<activity
android:name=".VisibleClusteringDemoActivity"
android:exported="true" />
<activity
android:name=".CustomMarkerClusteringDemoActivity"
android:exported="true" />
<activity
android:name=".CustomAdvancedMarkerClusteringDemoActivity"
android:exported="true" />
<activity
android:name=".ZoomClusteringDemoActivity"
android:exported="true" />
<activity
android:name=".ClusteringViewModelDemoActivity"
android:exported="true" />
<activity
android:name=".TileProviderAndProjectionDemo"
android:exported="true" />
<activity
android:name=".HeatmapsDemoActivity"
android:exported="true" />
<activity
android:name=".HeatmapsPlacesDemoActivity"
android:exported="true" />
<activity
android:name=".GeoJsonDemoActivity"
android:exported="true" />
<activity
android:name=".KmlDemoActivity"
android:exported="true" />
<activity
android:name=".MultiLayerDemoActivity"
android:exported="true" />
<activity
android:name=".AnimationUtilDemoActivity"
android:exported="true" />
<activity
android:name=".StreetViewDemoActivity"
android:exported="true" />

</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2024 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.google.maps.android.utils.demo

import android.content.Context
import android.content.pm.PackageManager
import java.util.regex.Pattern

/**
* Checks if the provided context has a valid Google Maps API key in its metadata.
*
*
* This method retrieves the API key from the application's metadata and returns whether or
* not it has a valid format.
*
* @param context The context to check for the API key.
* @return `true` if the context has a valid API key, `false` otherwise.
*/
fun hasMapsApiKey(context: Context): Boolean {
val mapsApiKey = getMapsApiKey(context)

return mapsApiKey != null && keyHasValidFormat(mapsApiKey)
}

/**
* Checks if the provided API key has a valid format.
*
*
* The valid format is defined by the regular expression "^AIza[0-9A-Za-z\\-_]{35}$".
*
* @param apiKey The API key to validate.
* @return `true` if the API key has a valid format, `false` otherwise.
*/
internal fun keyHasValidFormat(apiKey: String): Boolean {
val regex = "^AIza[0-9A-Za-z\\-_]{35}$"
val pattern = Pattern.compile(regex)
val matcher = pattern.matcher(apiKey)
return matcher.matches()
}

/**
* Retrieves the Google Maps API key from the application metadata.
*
* @param context The context to retrieve the API key from.
* @return The API key if found, `null` otherwise.
*/
private fun getMapsApiKey(context: Context): String? {
try {
val bundle = context.packageManager
.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA)
.metaData
return bundle.getString("com.google.android.geo.API_KEY")
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package com.google.maps.android.utils.demo;

import static com.google.maps.android.utils.demo.ApiKeyValidatorKt.hasMapsApiKey;

import android.os.Bundle;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
Expand All @@ -36,6 +39,12 @@ protected int getLayoutId() {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (!hasMapsApiKey(this)) {
Toast.makeText(this, R.string.bad_maps_api_key, Toast.LENGTH_LONG).show();
finish();
}

mIsRestore = savedInstanceState != null;
setContentView(getLayoutId());
setUpMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

public class ClusteringViewModel extends ViewModel {

private NonHierarchicalViewBasedAlgorithm<MyItem> mAlgorithm = new NonHierarchicalViewBasedAlgorithm<>(0, 0);
private final NonHierarchicalViewBasedAlgorithm<MyItem> mAlgorithm = new NonHierarchicalViewBasedAlgorithm<>(0, 0);

NonHierarchicalViewBasedAlgorithm<MyItem> getAlgorithm() {
return mAlgorithm;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.google.maps.android.utils.demo;

import static com.google.maps.android.utils.demo.ApiKeyValidatorKt.hasMapsApiKey;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
Expand All @@ -32,12 +34,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

if (BuildConfig.MAPS_API_KEY.isEmpty()) {
Toast.makeText(this, "Add your own API key in local.properties as MAPS_API_KEY=YOUR_API_KEY", Toast.LENGTH_LONG).show();
if (!hasMapsApiKey(this)) {
Toast.makeText(this, R.string.bad_maps_api_key, Toast.LENGTH_LONG).show();
finish();
}

setContentView(R.layout.main);

mListView = findViewById(R.id.list);

addDemo("Clustering", ClusteringDemoActivity.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.google.maps.android.utils.demo
import android.app.Activity
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.StreetViewUtils
import kotlinx.coroutines.DelicateCoroutinesApi
Expand All @@ -33,6 +34,11 @@ class StreetViewDemoActivity : Activity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.street_view_demo)

if (!hasMapsApiKey(this)) {
Toast.makeText(this, R.string.bad_maps_api_key, Toast.LENGTH_LONG).show()
finish()
}

GlobalScope.launch(Dispatchers.Main) {
val response1 =
StreetViewUtils.fetchStreetViewData(LatLng(48.1425918, 11.5386121), BuildConfig.MAPS_API_KEY)
Expand All @@ -43,4 +49,3 @@ class StreetViewDemoActivity : Activity() {
}
}
}

27 changes: 14 additions & 13 deletions demo/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google Inc.
~
~ 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.
Copyright 2024 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>
Expand All @@ -33,4 +33,5 @@
<string name="button_radius">Radius</string>
<string name="button_gradient">Gradient</string>
<string name="button_opacity">Opacity</string>
<string name="bad_maps_api_key">Invalid or missing Google Maps API key</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2024 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.google.maps.android.utils.demo

import org.junit.Test
import com.google.common.truth.Truth.assertThat

class ApiKeyValidatorTest {

@Test
fun testValidKey() {
val apiKey = "AIzaSyBZAuYobWtoFlmuyyG2HxQWatnPJZ79_BW"
assertThat(keyHasValidFormat(apiKey)).isTrue()
}

@Test
fun testInvalidKeys() {
assertThat(keyHasValidFormat("")).isFalse()
assertThat(keyHasValidFormat("YOUR_API_KEY")).isFalse()
val apiKey = "AIzaSyBZAuYobWtoFlmuyyG2HxQWatnPJZ79_BW"
assertThat(keyHasValidFormat(apiKey.dropLast(1))).isFalse()
}

}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
networkTimeout=10000
validateDistributionUrl=true
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit fc7bada

Please sign in to comment.