From e9e20bd41fd0322fa24430db4dd73c53d7412e95 Mon Sep 17 00:00:00 2001
From: dkhawk <107309+dkhawk@users.noreply.github.com>
Date: Tue, 8 Oct 2024 13:23:42 -0600
Subject: [PATCH] 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
---
build.gradle | 4 +-
demo/build.gradle | 4 +-
demo/src/main/AndroidManifest.xml | 85 +++++++++++++------
.../android/utils/demo/ApiKeyValidator.kt | 55 ++++++++++++
.../android/utils/demo/BaseDemoActivity.java | 9 ++
.../utils/demo/ClusteringViewModel.java | 2 +-
.../maps/android/utils/demo/MainActivity.java | 10 ++-
.../utils/demo/StreetViewDemoActivity.kt | 7 +-
demo/src/main/res/values/strings.xml | 1 +
gradle/wrapper/gradle-wrapper.properties | 2 +-
library/build.gradle | 4 +-
11 files changed, 146 insertions(+), 37 deletions(-)
create mode 100644 demo/src/main/java/com/google/maps/android/utils/demo/ApiKeyValidator.kt
diff --git a/build.gradle b/build.gradle
index 747ad32e2..9c288e84e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,7 +15,7 @@
*/
buildscript {
- ext.kotlin_version = '1.9.24'
+ ext.kotlin_version = '2.0.0'
repositories {
google()
mavenCentral()
@@ -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"
diff --git a/demo/build.gradle b/demo/build.gradle
index fb00f544f..71b6e50e0 100644
--- a/demo/build.gradle
+++ b/demo/build.gradle
@@ -53,10 +53,10 @@ 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'
diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml
index 168d2bc16..e6cb01240 100644
--- a/demo/src/main/AndroidManifest.xml
+++ b/demo/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
-
-
@@ -58,35 +56,72 @@
android:value="${MAPS_API_KEY}" />
+ android:exported="true">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/ApiKeyValidator.kt b/demo/src/main/java/com/google/maps/android/utils/demo/ApiKeyValidator.kt
new file mode 100644
index 000000000..14ee154fc
--- /dev/null
+++ b/demo/src/main/java/com/google/maps/android/utils/demo/ApiKeyValidator.kt
@@ -0,0 +1,55 @@
+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.
+ */
+private 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
+ }
+}
\ No newline at end of file
diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/BaseDemoActivity.java b/demo/src/main/java/com/google/maps/android/utils/demo/BaseDemoActivity.java
index 60e497b87..67cc61043 100644
--- a/demo/src/main/java/com/google/maps/android/utils/demo/BaseDemoActivity.java
+++ b/demo/src/main/java/com/google/maps/android/utils/demo/BaseDemoActivity.java
@@ -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;
@@ -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();
diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringViewModel.java b/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringViewModel.java
index ddd9c0f08..c3baad333 100644
--- a/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringViewModel.java
+++ b/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringViewModel.java
@@ -30,7 +30,7 @@
public class ClusteringViewModel extends ViewModel {
- private NonHierarchicalViewBasedAlgorithm mAlgorithm = new NonHierarchicalViewBasedAlgorithm<>(0, 0);
+ private final NonHierarchicalViewBasedAlgorithm mAlgorithm = new NonHierarchicalViewBasedAlgorithm<>(0, 0);
NonHierarchicalViewBasedAlgorithm getAlgorithm() {
return mAlgorithm;
diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.java b/demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.java
index fcd22813d..8cf7561fb 100644
--- a/demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.java
+++ b/demo/src/main/java/com/google/maps/android/utils/demo/MainActivity.java
@@ -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;
@@ -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);
diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/StreetViewDemoActivity.kt b/demo/src/main/java/com/google/maps/android/utils/demo/StreetViewDemoActivity.kt
index 3b81f0251..52e634766 100644
--- a/demo/src/main/java/com/google/maps/android/utils/demo/StreetViewDemoActivity.kt
+++ b/demo/src/main/java/com/google/maps/android/utils/demo/StreetViewDemoActivity.kt
@@ -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
@@ -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)
@@ -43,4 +49,3 @@ class StreetViewDemoActivity : Activity() {
}
}
}
-
diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml
index 7522cb78a..7f18a6842 100644
--- a/demo/src/main/res/values/strings.xml
+++ b/demo/src/main/res/values/strings.xml
@@ -33,4 +33,5 @@
Radius
Gradient
Opacity
+ Invalid or missing Google Maps API key
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0d746b8fa..078ba817c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -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
diff --git a/library/build.gradle b/library/build.gradle
index e2227a066..4a3995cfe 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -64,9 +64,9 @@ android {
dependencies {
// We are adding api() for the Maps SDK for Android, so it propagates to the app-level modules.
- api 'com.google.android.gms:play-services-maps:18.2.0'
+ api 'com.google.android.gms:play-services-maps:19.0.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
- implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.core:core-ktx:1.13.1'
lintPublish project(':lint-checks')
testImplementation 'junit:junit:4.13.2'