diff --git a/README.md b/README.md index 7785f7174..817ec934b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ range of applications using the [Google Maps SDK for Android][android-site]. computeArea - **KML** — displays KML data - **GeoJSON** — displays and styles GeoJSON data +- **StreetView Utility** — checks if a given StreetView location exists

@@ -289,6 +290,20 @@ _Old_ +## Usage guide + +The full documentation can be found here [Google Maps Platform documentation][devsite-guide]. + +For a quick snippet on the StreetViewUtil class, keep reading. + +The StreetViewUtil class provides functionality to check whether a location is supported in StreetView. To call it, use the following snippet: + +```kotlin +StreetViewUtils.fetchStreetViewData(LatLng(8.1425918, 11.5386121), BuildConfig.MAPS_API_KEY) +``` + +`fetchStreetViewData` will return `NOT_FOUND`, `OK` or `ZERO_RESULTS`, depending on the response. + ## Support Encounter an issue while using this library? diff --git a/build.gradle b/build.gradle index aa0471d75..b30d607ba 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.3.1' classpath 'com.hiya:jacoco-android:0.2' classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0" } } diff --git a/demo/build.gradle b/demo/build.gradle index 9e9a785d4..0ef9044a6 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -54,8 +54,12 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.7.0-alpha01' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0' + // [END_EXCLUDE] } // [END maps_android_utils_install_snippet] diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 09dc1de90..fd890c5e6 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -68,6 +68,7 @@ + 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 0d86c4aaf..26a7a728e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013 Google Inc. + * Copyright 2023 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { addDemo("KML Layer Overlay", KmlDemoActivity.class); addDemo("Multi Layer", MultiLayerDemoActivity.class); addDemo("AnimationUtil sample", AnimationUtilDemoActivity.class); + addDemo("Street View Demo", StreetViewDemoActivity.class); } private void addDemo(String demoName, Class activityClass) { diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/SampleManager.java b/demo/src/main/java/com/google/maps/android/utils/demo/SampleManager.java deleted file mode 100644 index 1f4637022..000000000 --- a/demo/src/main/java/com/google/maps/android/utils/demo/SampleManager.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.google.maps.android.utils.demo; - -import com.google.android.gms.maps.GoogleMap; -import com.google.maps.android.collections.GroundOverlayManager; - - -import androidx.annotation.NonNull; - -public class SampleManager extends GroundOverlayManager implements GoogleMap.OnGroundOverlayClickListener { - - - public SampleManager(@NonNull GoogleMap map) { - super(map); - } -} 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 new file mode 100644 index 000000000..fb642a377 --- /dev/null +++ b/demo/src/main/java/com/google/maps/android/utils/demo/StreetViewDemoActivity.kt @@ -0,0 +1,30 @@ +package com.google.maps.android.utils.demo + +import android.app.Activity +import android.os.Bundle +import android.widget.TextView +import com.google.android.gms.maps.model.LatLng +import com.google.maps.android.StreetViewUtils +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class StreetViewDemoActivity : Activity() { + + @OptIn(DelicateCoroutinesApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.street_view_demo) + + GlobalScope.launch(Dispatchers.Main) { + val response1 = + StreetViewUtils.fetchStreetViewData(LatLng(48.1425918, 11.5386121), BuildConfig.MAPS_API_KEY) + val response2 = StreetViewUtils.fetchStreetViewData(LatLng(8.1425918, 11.5386121), BuildConfig.MAPS_API_KEY) + + findViewById(R.id.textViewFirstLocation).text = "Location 1 is supported in StreetView: $response1" + findViewById(R.id.textViewSecondLocation).text = "Location 2 is supported in StreetView: $response2" + } + } +} + diff --git a/demo/src/main/res/layout/street_view_demo.xml b/demo/src/main/res/layout/street_view_demo.xml new file mode 100644 index 000000000..1aada33de --- /dev/null +++ b/demo/src/main/res/layout/street_view_demo.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/library/build.gradle b/library/build.gradle index 1cabd08db..6c846ac37 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -15,6 +15,7 @@ */ plugins { id 'kotlin-android' + id 'org.jetbrains.kotlin.android' } android { @@ -56,11 +57,14 @@ android { dependencies { implementation 'com.google.android.gms:play-services-maps:18.1.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0' implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.core:core-ktx:1.10.1' lintPublish project(':lint-checks') testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.7.3' + testImplementation 'org.robolectric:robolectric:4.10.3' testImplementation 'net.sf.kxml:kxml2:2.3.0' + testImplementation "io.mockk:mockk:1.13.4" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } diff --git a/library/src/main/java/com/google/maps/android/StreetViewUtil.kt b/library/src/main/java/com/google/maps/android/StreetViewUtil.kt new file mode 100644 index 000000000..945648aae --- /dev/null +++ b/library/src/main/java/com/google/maps/android/StreetViewUtil.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2023 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. + */ +package com.google.maps.android + +import kotlinx.coroutines.withContext +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL +import com.google.android.gms.maps.model.LatLng +import kotlinx.coroutines.Dispatchers +import org.json.JSONObject +import java.io.BufferedReader +import java.io.InputStreamReader + + +/** + * Utility functions for StreetView + */ +class StreetViewUtils { + companion object { + + /** + * This function will check whether a location is available on StreetView or not. + * + * @param latLng Location to check + * @param apiKey Maps API Key + * @return A boolean value specifying if the location is available on Street View or not. + */ + suspend fun fetchStreetViewData(latLng: LatLng, apiKey: String): Status { + val urlString = + "https://maps.googleapis.com/maps/api/streetview/metadata?location=${latLng.latitude},${latLng.longitude}&key=$apiKey" + + return withContext(Dispatchers.IO) { + try { + val url = URL(urlString) + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + + val responseCode = connection.responseCode + if (responseCode == HttpURLConnection.HTTP_OK) { + val inputStream = connection.inputStream + val bufferedReader = BufferedReader(InputStreamReader(inputStream)) + val responseString = bufferedReader.use { it.readText() } + bufferedReader.close() + inputStream.close() + deserializeResponse(responseString).status + } else { + throw IOException("HTTP Error: $responseCode") + } + } catch (e: IOException) { + e.printStackTrace() + throw IOException("Network error: ${e.message}") + } + } + } + + private fun deserializeResponse(responseString: String): ResponseStreetView { + val jsonObject = JSONObject(responseString) + val statusString = jsonObject.optString("status") + val status = Status.valueOf(statusString) + + return ResponseStreetView(status) + } + } +} + +data class ResponseStreetView(val status: Status) + +enum class Status { + OK, + ZERO_RESULTS, + NOT_FOUND +} diff --git a/library/src/test/java/com/google/maps/android/StreetViewUtilTest.kt b/library/src/test/java/com/google/maps/android/StreetViewUtilTest.kt new file mode 100644 index 000000000..0a150f768 --- /dev/null +++ b/library/src/test/java/com/google/maps/android/StreetViewUtilTest.kt @@ -0,0 +1,39 @@ +package com.google.maps.android + +import com.google.android.gms.maps.model.LatLng +import io.mockk.coEvery +import io.mockk.mockkObject +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class StreetViewUtilsTest { + + lateinit var latLng : LatLng + + val apiKey = "AN_API_KEY" + + @Before + fun setUp() { + latLng = LatLng(37.7749, -122.4194) // San Francisco coordinates + + // Mock the network behavior using MockK + mockkObject(StreetViewUtils) + coEvery { StreetViewUtils.fetchStreetViewData(any(), any()) } returns Status.NOT_FOUND + coEvery { StreetViewUtils.fetchStreetViewData(latLng, apiKey) } returns Status.OK + } + + @Test + fun testLocationFoundOnStreetView() = runBlocking { + val status = StreetViewUtils.fetchStreetViewData(latLng, apiKey) + assertEquals(Status.OK, status) + } + + @Test + fun testLocationNotFoundOnStreetView() = runBlocking { + val status = StreetViewUtils.fetchStreetViewData(LatLng(10.0, 20.0), apiKey) + assertEquals(Status.NOT_FOUND, status) + } +} + diff --git a/local.defaults.properties b/local.defaults.properties index 818d21b20..420e23930 100644 --- a/local.defaults.properties +++ b/local.defaults.properties @@ -1 +1 @@ -MAPS_API_KEY="YOUR_API_KEY" \ No newline at end of file +MAPS_API_KEY="AIzaSyA4SCF8hbjd5M3pq-FncAtSu1uTe_yaQBg" \ No newline at end of file