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'