diff --git a/app/build.gradle b/app/build.gradle index 5d78971..9c8f6b3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'kotlin-android' + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' } android { @@ -42,4 +43,12 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} + +secrets { + // To add your Places API key to this project: + // 1. Update a file in your root project called `local.properties` and add this line, + // where YOUR_API_KEY is your API key: + // PLACES_API_KEY=YOUR_API_KEY + defaultPropertiesFileName 'local.defaults.properties' } \ No newline at end of file diff --git a/app/src/main/java/com/github/urmichm/demo/MainActivity.kt b/app/src/main/java/com/github/urmichm/demo/MainActivity.kt index 865a052..b8ba202 100644 --- a/app/src/main/java/com/github/urmichm/demo/MainActivity.kt +++ b/app/src/main/java/com/github/urmichm/demo/MainActivity.kt @@ -2,13 +2,19 @@ package com.github.urmichm.demo import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import com.github.urmichm.placesearchktx.Diana +import android.util.Log +import com.github.urmichm.placesearchktx.placesearch.FindPlace +import com.github.urmichm.placesearchktx.placesearch.NearbySearch +import com.github.urmichm.placesearchktx.placesearch.TextSearch class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Diana.hello() + + Log.i("MainActivity" ,FindPlace.TAG) + Log.i("MainActivity" ,NearbySearch.TAG) + Log.i("MainActivity" , TextSearch.TAG) + setContentView(R.layout.activity_main) - Diana.bye() } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index eea3e48..71f7952 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31" + classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/local.defaults.properties b/local.defaults.properties new file mode 100644 index 0000000..7825929 --- /dev/null +++ b/local.defaults.properties @@ -0,0 +1,4 @@ +# This file contains a default value for your GMP API Key. +# To provide your actual GMP API Key, update the local.properties file using the PLACES_API_KEY +# as demonstrated below. +PLACES_API_KEY="YOUR_API_KEY" diff --git a/placesearch-ktx/build.gradle b/placesearch-ktx/build.gradle index d40777f..196c071 100644 --- a/placesearch-ktx/build.gradle +++ b/placesearch-ktx/build.gradle @@ -2,6 +2,7 @@ plugins { id 'com.android.library' id 'kotlin-android' id 'maven-publish' + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' } android { @@ -75,4 +76,12 @@ afterEvaluate { } } } -} \ No newline at end of file +} + +secrets { + // To add your Places API key to this project: + // 1. Update a file in your root project called `local.properties` and add this line, + // where YOUR_API_KEY is your API key: + // PLACES_API_KEY=YOUR_API_KEY + defaultPropertiesFileName 'local.defaults.properties' +} diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/Diana.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/Diana.kt deleted file mode 100644 index b2b6e8f..0000000 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/Diana.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.github.urmichm.placesearchktx - -import android.util.Log - -/** - * Main class where all intialization takes place - * - * - * Place Search requests return a subset of the fields that are returned by Place Details requests. - * If the field you want is not returned by Place Search, you can use Place Search to get a place_id, - * then use that Place ID to make a Place Details request. - * */ -class Diana private constructor( - builder : Builder, - internal val key: String -){ - - init{ - vicinityAsAddress = builder.getVicinityAsAddress() - } - - companion object { - private const val TAG = "PlaceSearch-KTX" - - /** - * Output format; indicates output in JavaScript Object Notation (JSON) - * */ - const val OUTPUT_FORMAT = "json" - - /** - * Convert vicinity data into Address data when converting Container objects into Google's Place object - * */ - var vicinityAsAddress = true - - fun hello(){ - Log.i(TAG, "Hello Diana!") - } - - fun bye(){ - Log.i(TAG, "Bye Diana!") - } - } - - /** - * The builder class for [Diana] class - * @param key Your application's API key. This key identifies your application. - * */ - class Builder(private val key : String){ - - /** - * Convert vicinity data into Address data when converting Container objects into Google's Place object - * */ - private var vicinityAsAddress = true - /** Getter for [vicinityAsAddress] */ - fun getVicinityAsAddress() = this.vicinityAsAddress - /** Setter for [vicinityAsAddress] */ - fun setVicinityAsAddress(vicinity2Address : Boolean) = apply{this.vicinityAsAddress = vicinity2Address} - - /** - * The build method to create a [Diana] object - * @return [Diana] object created according to the builder settings. - * */ - fun build() : Diana{ - return Diana(this, key) - } - } - - -} \ No newline at end of file diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/Message.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/Message.kt deleted file mode 100644 index 8510090..0000000 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/Message.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.urmichm.placesearchktx - -/** - * Message class to be return after validation of the request parameters. - * @param message Message of the validation - * @param isValid true if the validation was successful; false otherwise - * */ -class Message(val message : String, val isValid : Boolean ) \ No newline at end of file diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/containers/common/PlaceDetailsContainer.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/containers/common/PlaceDetailsContainer.kt index 3fd0909..9dd982d 100644 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/containers/common/PlaceDetailsContainer.kt +++ b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/containers/common/PlaceDetailsContainer.kt @@ -1,7 +1,5 @@ package com.github.urmichm.placesearchktx.containers.common -import android.os.Build -import com.github.urmichm.placesearchktx.Diana import com.google.android.libraries.places.api.model.Place import com.squareup.moshi.Json @@ -50,7 +48,6 @@ data class PlaceDetailsContainer( /** * Converts [PlaceDetailsContainer] into [Place] object - * @warning Requires API [Build.VERSION_CODES.N] * */ fun asPlace() : Place{ val placeBuilder = Place.builder() @@ -67,8 +64,6 @@ data class PlaceDetailsContainer( .setIconUrl(this.iconUrl) // .setPhotoMetadatas(this.photos?.stream()?.map { it.toPhotoMetadata() }?.toList()) .setPlusCode(this.plusCode?.toPlusCode()) - - // TODO: deal with formattedAddress vs vicinity .setAddress(this.formattedAddress) /* The Following fields are not returned by Place Search, Nearby Search, and Text Search */ @@ -80,8 +75,9 @@ data class PlaceDetailsContainer( // .setUtcOffsetMinutes(@Nullable Integer var1); // .setWebsiteUri(@Nullable Uri var1); - if(null == placeBuilder.address && Diana.vicinityAsAddress) { - placeBuilder.address = this.vicinity + if(null == placeBuilder.address && null != this.vicinity) { + println("WARN: Vicinity is used as address") + placeBuilder.setAddress(this.vicinity) } return placeBuilder.build() diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/network/Service.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/network/Service.kt index 097e4f8..9f88c92 100644 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/network/Service.kt +++ b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/network/Service.kt @@ -1,6 +1,6 @@ package com.github.urmichm.placesearchktx.network -import com.github.urmichm.placesearchktx.Diana.Companion.OUTPUT_FORMAT +import com.github.urmichm.placesearchktx.BuildConfig import com.github.urmichm.placesearchktx.containers.FindPlaceContainer import com.github.urmichm.placesearchktx.containers.NearbySearchContainer import com.github.urmichm.placesearchktx.containers.TextSearchContainer @@ -14,6 +14,10 @@ import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.http.GET import retrofit2.http.Query +/** + * API key + * */ +private val PLACES_API_KEY : String = BuildConfig.PLACES_API_KEY /** * Nearby search requests @@ -23,6 +27,10 @@ import retrofit2.http.Query * */ private val URL = "https://maps.googleapis.com/maps/api/place/" +/** + * Output format; indicates output in JavaScript Object Notation (JSON) + * */ +private const val OUTPUT_FORMAT = "json" /** * @details https://developers.google.com/maps/documentation/places/web-service/search#PlaceSearchRequests @@ -55,7 +63,7 @@ internal interface DianaService{ * */ @GET("textsearch/${OUTPUT_FORMAT}") fun textSearch( - @Query("key") key : String, + @Query("key") key : String = PLACES_API_KEY, @Query("query") query : String, @Query("language") language :String?, @@ -95,7 +103,7 @@ internal interface DianaService{ * */ @GET("nearbysearch/${OUTPUT_FORMAT}") fun nearbySearch( - @Query("key") key : String, + @Query("key") key : String = PLACES_API_KEY, @Query("location") location : String, @Query("keyword") keyword :String?, @Query("language") language :String?, @@ -133,7 +141,7 @@ internal interface DianaService{ * */ @GET("findplacefromtext/${OUTPUT_FORMAT}") fun findPlace( - @Query("key") key : String, + @Query("key") key : String = PLACES_API_KEY, @Query("input") input : String, @Query("inputtype") inputtype :String, @@ -157,7 +165,7 @@ private val moshi = Moshi.Builder() /** * Main entry point for network access. - * [Network.diana].nearbySearch(..) + * [Network.service].nearbySearch(..) */ internal object Network { // Configure retrofit to parse JSON and use coroutines @@ -167,6 +175,6 @@ internal object Network { .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() - val diana = retrofit.create(DianaService::class.java) as DianaService + val service = retrofit.create(DianaService::class.java) as DianaService } diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/FindPlace.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/FindPlace.kt index 9e81b8c..62539dd 100644 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/FindPlace.kt +++ b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/FindPlace.kt @@ -1,6 +1,5 @@ package com.github.urmichm.placesearchktx.placesearch -import com.github.urmichm.placesearchktx.Diana import com.github.urmichm.placesearchktx.containers.FindPlaceContainer import com.github.urmichm.placesearchktx.network.Network import com.github.urmichm.placesearchktx.toRequestString @@ -16,58 +15,14 @@ import kotlinx.coroutines.Deferred * */ class FindPlace private constructor(private val builder :Builder) { - /** - * [Diana] object with general settings - * */ - private val diana : Diana = builder.diana; - - /** - * The required parameter. - * The text string on which to search, for example: "restaurant" or "123 Main Street". - * This must be a place name, address, or category of establishments. - * Any other types of input can generate errors and are not guaranteed to return valid results. - * The Places API will return candidate matches based on this string and order the results based - * on their perceived relevance. - * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#input - * */ private val input :String = builder.getInput() - - /** - * The required parameter. - * The type of input. This can be one of either [textquery] or [phonenumber]. - * Phone numbers must be in international format (prefixed by a plus sign ("+"), - * followed by the country code, then the phone number itself). - * See E.164 ITU recommendation for more information. - * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#inputtype - * */ private val inputtype :String = builder.getInputType().toString() - - /** - * Use the fields parameter to specify a comma-separated list of place data types to return. - * For example: fields=formatted_address,name,geometry. - * Use a forward slash when specifying compound values. For example: opening_hours/open_now. - * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#fields - * */ private var fields :String? = builder.getFields() - - /** - * The language in which to return results. - * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#language - * */ private var language :String? = builder.getLanguage() - - /** - * Prefer results in a specified area, by specifying either a radius plus lat/lng, or two lat/lng pairs representing the points of a rectangle. If this parameter is not specified, the API uses IP address biasing by default. - * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#locationbias - * */ private var locationbias :String? = builder.getLocationBias() - /** - * The builder class for [FindPlace] class - * @param builder The [Builder] object - * */ - class Builder(val diana : Diana){ + class Builder() { /** * The required parameter. @@ -78,7 +33,7 @@ class FindPlace private constructor(private val builder :Builder) { * on their perceived relevance. * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#input * */ - private lateinit var input :String + private lateinit var input: String /** * Getter for [input] @@ -90,7 +45,7 @@ class FindPlace private constructor(private val builder :Builder) { * Getter for [input] * @param input The text string on which to search, for example: "restaurant" or "123 Main Street". * */ - fun setInput(input :String) :Builder = apply { + fun setInput(input: String): Builder = apply { this.input = input } @@ -102,7 +57,7 @@ class FindPlace private constructor(private val builder :Builder) { * See E.164 ITU recommendation for more information. * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#inputtype * */ - private lateinit var inputtype :InputType + private lateinit var inputtype: InputType /** * Getter for [inputtype] @@ -114,7 +69,7 @@ class FindPlace private constructor(private val builder :Builder) { * Setter for [inputtype] * @param inputType The type of input. This can be one of either [InputType.TEXTQUERY] or [InputType.PHONENUMBER]. * */ - fun setInputType(inputType: InputType) :Builder = apply { + fun setInputType(inputType: InputType): Builder = apply { this.inputtype = inputType } @@ -124,7 +79,7 @@ class FindPlace private constructor(private val builder :Builder) { * Uses a forward slash when specifying compound values. For example: opening_hours/open_now. * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#fields * */ - private var fields :String? = null + private var fields: String? = null /** * Getter for [fields] @@ -136,7 +91,7 @@ class FindPlace private constructor(private val builder :Builder) { * Setter for [fields] * @param fields List of [FindPlace.Field] enums to specify a list of place data types to return. * */ - fun setFields(fields : List): Builder = apply { + fun setFields(fields: List): Builder = apply { this.fields = fields.joinToString(separator = ",") } @@ -145,7 +100,7 @@ class FindPlace private constructor(private val builder :Builder) { * @param fields List of [Place.Field] enums to specify a list of place data types to return. * */ @JvmName("googleSetFields") - fun setFields(fields : List) : Builder = apply { + fun setFields(fields: List): Builder = apply { this.fields = fields.joinToString(separator = ",", transform = { it.toRequestString() }) @@ -156,7 +111,7 @@ class FindPlace private constructor(private val builder :Builder) { * The language in which to return results. * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#language * */ - private var language :String? = null + private var language: String? = null /** * Getter for [language] @@ -168,7 +123,7 @@ class FindPlace private constructor(private val builder :Builder) { * Setter for [language] * @param language value to be set * */ - fun setLanguage(language :String) :Builder = apply { + fun setLanguage(language: String): Builder = apply { this.language = language } @@ -176,7 +131,7 @@ class FindPlace private constructor(private val builder :Builder) { * Prefer results in a specified area, by specifying either a radius plus lat/lng, or two lat/lng pairs representing the points of a rectangle. If this parameter is not specified, the API uses IP address biasing by default. * @details https://developers.google.com/maps/documentation/places/web-service/search-find-place#locationbias * */ - private var locationbias : String? = null + private var locationbias: String? = null /** * Getter for [locationbias] @@ -190,7 +145,7 @@ class FindPlace private constructor(private val builder :Builder) { * @param radius A string specifying radius in meters * @param center The center of the circle, lat/lng in decimal degrees. */ - fun setLocationBias(radius : Double, center : LatLng) :Builder = apply { + fun setLocationBias(radius: Double, center: LatLng): Builder = apply { locationbias = "circle:${radius}@${center.latitude},${center.longitude}" } @@ -200,7 +155,7 @@ class FindPlace private constructor(private val builder :Builder) { * Uses the following format: point:lat,lng. * @param point - A single lat/lng coordinate. */ - fun setLocationBias(point : LatLng) :Builder = apply { + fun setLocationBias(point: LatLng): Builder = apply { locationbias = "point:${point.latitude},${point.longitude}" } @@ -211,7 +166,7 @@ class FindPlace private constructor(private val builder :Builder) { * Uses the following format:rectangle:south,west|north,east. * Note that east/west values are wrapped to the range -180, 180, and north/south values are clamped to the range -90, 90. */ - fun setLocationBias(southwest : LatLng, northeast : LatLng) :Builder = apply{ + fun setLocationBias(southwest: LatLng, northeast: LatLng): Builder = apply { locationbias = "rectangle:${southwest.latitude},${southwest.longitude}|${northeast.latitude},${northeast.longitude}" } @@ -222,7 +177,7 @@ class FindPlace private constructor(private val builder :Builder) { * Uses the following format:rectangle:south,west|north,east. * Note that east/west values are wrapped to the range -180, 180, and north/south values are clamped to the range -90, 90. */ - fun setLocationBias(bounds : RectangularBounds) :Builder = apply{ + fun setLocationBias(bounds: RectangularBounds): Builder = apply { locationbias = "rectangle:${bounds.southwest.latitude},${bounds.southwest.longitude}" + "|${bounds.northeast.latitude},${bounds.northeast.longitude}" @@ -233,7 +188,18 @@ class FindPlace private constructor(private val builder :Builder) { * The build method to create a [FindPlace] object * @return [FindPlace] object created according to the builder settings. * */ - fun build() : FindPlace = FindPlace(this) + fun build(): FindPlace { + validate() + return FindPlace(this) + } + + /** + * Validate parameters before calling the server + * @throws IllegalArgumentException if NearbySearch object is not valid + * */ + private fun validate() { + + } } /** @@ -302,8 +268,7 @@ class FindPlace private constructor(private val builder :Builder) { suspend fun call(): FindPlaceContainer?{ val find : Deferred = - Network.diana.findPlace( - key = diana.key, + Network.service.findPlace( input = input, inputtype = inputtype, fields = fields, @@ -319,4 +284,10 @@ class FindPlace private constructor(private val builder :Builder) { } } + + companion object{ + public const val TAG = "FindPlace" + } + + } \ No newline at end of file diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/NearbySearch.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/NearbySearch.kt index 1d35e71..cf3350f 100644 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/NearbySearch.kt +++ b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/NearbySearch.kt @@ -17,75 +17,19 @@ import kotlin.Exception * */ class NearbySearch private constructor(private val builder : Builder){ - /** - * [Diana] object with general settings - * */ - private val diana : Diana = builder.diana - - /** - * The required parameter. - * The [LatLng] object describing latitude/longitude around which to retrieve place information. - * */ private val location :String = builder.getLocation() - - /** - * The text string on which to search, for example: "restaurant" or "123 Main Street". - * This must be a place name, address, or category of establishments. - * If this parameter is omitted, places with a business_status of CLOSED_TEMPORARILY or CLOSED_PERMANENTLY will not be returned. - * */ private val keyword :String? = builder.getKeyword() - - /** - * The language in which to return results. - * */ private val language :String? = builder.getLanguage() - - /** - * Restricts results to only those places within the specified range. - * Valid values range between 0 (most affordable) to 4 (most expensive), inclusive. - * */ private val maxPrice :Int? = builder.getMaxPrice() - - /** - * Restricts results to only those places within the specified range. - * Valid values range between 0 (most affordable) to 4 (most expensive), inclusive. - * */ private val minPrice :Int? = builder.getMinPrice() - - /** - * Returns only those places that are open for business at the time the query is sent. - * Places that do not specify opening hours in the Google Places database will not be returned if you include this parameter in your query. - * */ private val openNow :Boolean? = builder.getOpenNow() - - /** - * Returns up to 20 results from a previously run search. - * Setting a pagetoken parameter will execute a search with the same parameters used previously — all parameters other than pagetoken will be ignored. - * */ private val pageToken :String? = builder.getPageToken() - - /** - * Defines the distance (in meters) within which to return place results. - * Note that radius must not be included if [rankBy]=distance (described under Optional parameters below) is specified. - * */ private val radius :Int? = builder.getRadius() - - /** - * Specifies the order in which results are listed - * */ private val rankBy : Rankby? = builder.getRankBy() - - /** - * Restricts the results to places matching the specified type. Only one type may be specified. - * If more than one type is provided, all types following the first entry are ignored. - * */ private val type : Place.Type? = builder.getType() - /** - * The builder class for [NearbySearch] class - * @param diana [Diana] object with general settings - * */ - class Builder(val diana : Diana){ + + class Builder(){ /** * The required parameter. @@ -292,8 +236,39 @@ class NearbySearch private constructor(private val builder : Builder){ * @return [NearbySearch] object created according to the builder settings. * */ fun build() : NearbySearch { + validate() return NearbySearch(this) } + + /** + * Validate parameters before calling the server + * @throws IllegalArgumentException if NearbySearch object is not valid + * */ + private fun validate(){ + minPrice?.apply { + if( priceNotInRange(this) ) + throw IllegalArgumentException("minPrice is out of possible range.") + } + maxPrice?.apply { + if( priceNotInRange(this) ) + throw IllegalArgumentException("maxPrice is out of possible range.") + } + + when(rankBy){ + Rankby.PROMINENCE -> { + if(radius == null) + throw IllegalArgumentException( + "When prominence is specified, the radius parameter is required.") + + } + Rankby.DISTANCE -> { + if(radius != null) + throw IllegalArgumentException( + "When using rankBy=distance, the radius parameter will not be accepted, and will result in an INVALID_REQUEST.") + } + } + + } } /** @@ -302,13 +277,8 @@ class NearbySearch private constructor(private val builder : Builder){ * */ suspend fun call() : NearbySearchContainer? { - // TODO: validate on build, not on call! - val message = validate() - if(!message.isValid) throw Exception(message.message) - val nearby: Deferred = - Network.diana.nearbySearch( - key = diana.key, + Network.service.nearbySearch( location = location, keyword = keyword, language = language, @@ -330,39 +300,6 @@ class NearbySearch private constructor(private val builder : Builder){ } } - /** - * Validate parameters before calling the server - * @return The [Message] object based onn the validation. - * */ - fun validate() :Message{ - minPrice?.apply { - if( priceNotInRange(this) ) - return Message("minPrice is out of possible range.", false) - } - maxPrice?.apply { - if( priceNotInRange(this) ) - return Message("maxPrice is out of possible range.", false) - } - - when(rankBy){ - Rankby.PROMINENCE -> { - if(radius == null) - return Message( - "When prominence is specified, the radius parameter is required.", - false) - - } - Rankby.DISTANCE -> { - if(radius != null) - return Message( - "When using rankBy=distance, the radius parameter will not be accepted, and will result in an INVALID_REQUEST.", - false) - } - } - - return Message("OK", true) - } - /** * Rankby enumeration for [rankBy] parameter * */ @@ -375,5 +312,9 @@ class NearbySearch private constructor(private val builder : Builder){ } } + companion object{ + public const val TAG = "NearbySearch" + } + } \ No newline at end of file diff --git a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/TextSearch.kt b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/TextSearch.kt index 9f2a1f4..01f545d 100644 --- a/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/TextSearch.kt +++ b/placesearch-ktx/src/main/java/com/github/urmichm/placesearchktx/placesearch/TextSearch.kt @@ -1,8 +1,8 @@ package com.github.urmichm.placesearchktx.placesearch -import com.github.urmichm.placesearchktx.Diana import com.github.urmichm.placesearchktx.containers.TextSearchContainer import com.github.urmichm.placesearchktx.network.Network +import com.github.urmichm.placesearchktx.priceNotInRange import com.github.urmichm.placesearchktx.toRequestString import com.google.android.gms.maps.model.LatLng import com.google.android.libraries.places.api.model.Place @@ -16,29 +16,19 @@ import kotlin.Exception * */ class TextSearch private constructor(private val builder: Builder){ - private val diana : Diana = builder.diana - private val query :String = builder.getQuery() - private val language :String? = builder.getLanguage() - private val location : String? = builder.getLocation() - private var maxPrice :Int? = builder.getMaxPrice() - private var minPrice :Int? = builder.getMinPrice() - private var openNow :Boolean? = builder.getOpenNow() - private var pageToken :String? = builder.getPageToken() - private var radius :Int? = builder.getRadius() - private var region :String? = builder.getRegion() - private var type : Place.Type? = builder.getType() - class Builder(val diana : Diana){ + + class Builder() { /** * The text string on which to search, for example: "restaurant" or "123 Main Street". @@ -47,7 +37,7 @@ class TextSearch private constructor(private val builder: Builder){ * The Google Places service will return candidate matches based on this string and order the results based on * their perceived relevance. */ - private lateinit var query :String + private lateinit var query: String /** * Getter for [query] @@ -59,14 +49,14 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [query] * @param query The text string on which to search * */ - fun setQuery(query : String): Builder = apply{ + fun setQuery(query: String): Builder = apply { this.query = query } /** * The language in which to return results. * */ - private var language :String? = null + private var language: String? = null /** * Getter for [language] @@ -78,7 +68,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [language] * @param language New value for [language] * */ - fun setLanguage(language :String) : Builder = apply { + fun setLanguage(language: String): Builder = apply { this.language = language } @@ -86,7 +76,7 @@ class TextSearch private constructor(private val builder: Builder){ /** * The [LatLng] object describing latitude/longitude around which to retrieve place information. * */ - private var location : String? = null + private var location: String? = null /** * Getter for [location] in string format. @@ -98,7 +88,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [location] * @param location Location provided in form of [LatLng] object * */ - fun setLocation(location : LatLng): Builder = apply{ + fun setLocation(location: LatLng): Builder = apply { this.location = location.toRequestString() } @@ -107,7 +97,7 @@ class TextSearch private constructor(private val builder: Builder){ * Restricts results to only those places within the specified range. * Valid values range between 0 (most affordable) to 4 (most expensive), inclusive. * */ - private var maxPrice :Int? = null + private var maxPrice: Int? = null /** * Getter for [maxPrice] @@ -119,7 +109,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [maxPrice] * @param maxPrice The new value for [maxPrice] * */ - fun setMaxPrice(maxPrice :Int) :Builder = apply{ + fun setMaxPrice(maxPrice: Int): Builder = apply { this.maxPrice = maxPrice } @@ -128,7 +118,7 @@ class TextSearch private constructor(private val builder: Builder){ * Restricts results to only those places within the specified range. * Valid values range between 0 (most affordable) to 4 (most expensive), inclusive. * */ - private var minPrice :Int? = null + private var minPrice: Int? = null /** * Getter for [minPrice] @@ -140,7 +130,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [minPrice] * @param minPrice The new value for [minPrice] * */ - fun setMinPrice(minPrice :Int) :Builder = apply{ + fun setMinPrice(minPrice: Int): Builder = apply { this.minPrice = minPrice } @@ -149,7 +139,7 @@ class TextSearch private constructor(private val builder: Builder){ * Returns only those places that are open for business at the time the query is sent. * Places that do not specify opening hours in the Google Places database will not be returned if you include this parameter in your query. * */ - private var openNow :Boolean? = null + private var openNow: Boolean? = null /** * Getter for [openNow] @@ -161,7 +151,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [openNow] * @param openNow The new value for [openNow] * */ - fun setOpenNow(openNow :Boolean) :Builder = apply{ + fun setOpenNow(openNow: Boolean): Builder = apply { this.openNow = openNow } @@ -170,7 +160,7 @@ class TextSearch private constructor(private val builder: Builder){ * Returns up to 20 results from a previously run search. * Setting a page token parameter will execute a search with the same parameters used previously — all parameters other than pagetoken will be ignored. * */ - private var pageToken :String? = null + private var pageToken: String? = null /** * Getter for [pageToken] @@ -182,17 +172,16 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [pageToken] * @param pageToken The new value for [pageToken] * */ - fun setPageToken(pageToken :String) :Builder = apply{ + fun setPageToken(pageToken: String): Builder = apply { this.pageToken = pageToken } - /** * Defines the distance (in meters) within which to return place results. * Note that radius must not be included if [rankBy]=distance (described under Optional parameters below) is specified. * */ - private var radius :Int? = null + private var radius: Int? = null /** * Getter for [radius] @@ -204,7 +193,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [radius] * @param radius The new value for [radius] * */ - fun setRadius(radius :Int) :Builder = apply{ + fun setRadius(radius: Int): Builder = apply { this.radius = radius } @@ -212,7 +201,7 @@ class TextSearch private constructor(private val builder: Builder){ /** * The region code, specified as a ccTLD ("top-level domain") two-character value. * */ - private var region :String? = null + private var region: String? = null /** * Getter for [region] @@ -224,7 +213,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [region] * @param region The new value for [region] * */ - fun setRegion(region :String) :Builder = apply { + fun setRegion(region: String): Builder = apply { this.region = region } @@ -233,7 +222,7 @@ class TextSearch private constructor(private val builder: Builder){ * Restricts the results to places matching the specified type. Only one type may be specified. * If more than one type is provided, all types following the first entry are ignored. * */ - private var type : Place.Type? = null + private var type: Place.Type? = null /** * Getter for [type] @@ -245,7 +234,7 @@ class TextSearch private constructor(private val builder: Builder){ * Setter for [type] * @param type The new value for [type] * */ - fun setType(type : Place.Type) :Builder = apply{ + fun setType(type: Place.Type): Builder = apply { this.type = type } @@ -254,10 +243,29 @@ class TextSearch private constructor(private val builder: Builder){ * The build method to create a [TextSearch] object * @return [TextSearch] object created according to the builder settings. * */ - fun build() :TextSearch{ - // validate first! + fun build(): TextSearch { + validate() return TextSearch(this) } + + /** + * Validate parameters before calling the server + * @throws IllegalArgumentException if NearbySearch object is not valid + * */ + private fun validate(){ + minPrice?.apply { + if( priceNotInRange(this) ) + throw IllegalArgumentException("minPrice is out of possible range.") + } + maxPrice?.apply { + if( priceNotInRange(this) ) + throw IllegalArgumentException("maxPrice is out of possible range.") + } + region?.apply { + if(this.length > 2) + throw IllegalArgumentException("The region code, must be specified as a ccTLD (\"top-level domain\") TWO-character value.") + } + } } /** @@ -266,8 +274,7 @@ class TextSearch private constructor(private val builder: Builder){ * */ suspend fun call() : TextSearchContainer?{ val textSearch : Deferred = - Network.diana.textSearch( - key = diana.key, + Network.service.textSearch( query = query, language = language, location = location, @@ -287,4 +294,9 @@ class TextSearch private constructor(private val builder: Builder){ null } } + + companion object{ + public const val TAG = "TextSearch" + } + } \ No newline at end of file diff --git a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/DianaTest.kt b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/DianaTest.kt deleted file mode 100644 index 4b667bb..0000000 --- a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/DianaTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.github.urmichm.placesearchktx - -import org.junit.Assert.* - -import org.junit.Test - -class DianaTest { - - private val JsonFormat = "json" - - @Test - fun testDianaKey(){ - val key = "my-key" - val d = Diana.Builder(key).build() - assertEquals(key,d.key) - } - - @Test - fun testVicinityAsAddress(){ - // given default value - assertTrue(Diana.vicinityAsAddress) - - // override default value - Diana.vicinityAsAddress = false - assertFalse(Diana.vicinityAsAddress) - - // restore the default value - Diana.vicinityAsAddress = true - assertTrue(Diana.vicinityAsAddress) - } - - @Test - fun defaultOutputFormat(){ - assertEquals(JsonFormat, Diana.OUTPUT_FORMAT) - } - -// @Test -// fun nearbySearch() { -// val PLACES_API_KEY : String = YOUR_KEY -// var complete = false -// -// val type = "tourist_attraction" -// val latLng = "52.249787,21.012575" -// val rankby = "distance" -// -// val diana = Diana.Builder(PLACES_API_KEY) -// .setVicinity2Address(true) -// .build() -// -// CoroutineScope(Dispatchers.IO).launch{ -// val response = diana.nearbySearch(type, latLng, rankby) -// response?.let { -// assertEquals("OK", response.status) -// val p = response.results[0].asPlace() -// -//// println("All results:\n${response.results}") -//// println(p) -// -// assertEquals(response.results[0].name, p.name) -// complete = true -// } -// } -// -// Thread.sleep(5_000) -// assertTrue(complete) -// } -} \ No newline at end of file diff --git a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/containers/PlaceDetailsContainerTest.kt b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/containers/PlaceDetailsContainerTest.kt index 4057c02..88f3d12 100644 --- a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/containers/PlaceDetailsContainerTest.kt +++ b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/containers/PlaceDetailsContainerTest.kt @@ -1,6 +1,5 @@ package com.github.urmichm.placesearchktx.containers -import com.github.urmichm.placesearchktx.Diana import com.github.urmichm.placesearchktx.containers.common.* import com.google.android.libraries.places.api.model.Place import org.junit.Assert.* @@ -64,7 +63,7 @@ class PlaceDetailsContainerTest { assertEquals(container.businessStatus, place.businessStatus.name) assertEquals(container.rating, place.rating) - assertEquals( Diana.vicinityAsAddress, container.vicinity == place.address) + assertEquals( container.vicinity, place.address) assertEquals(container.placeId, place.id) assertEquals(container.userRatingsTotal, place.userRatingsTotal) diff --git a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/FindPlaceTest.kt b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/FindPlaceTest.kt index 39a8e66..35aa13c 100644 --- a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/FindPlaceTest.kt +++ b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/FindPlaceTest.kt @@ -1,26 +1,17 @@ package com.github.urmichm.placesearchktx.placesearch -import com.github.urmichm.placesearchktx.Diana import com.google.android.gms.maps.model.LatLng import com.google.android.libraries.places.api.model.Place import com.google.android.libraries.places.api.model.RectangularBounds import org.junit.Assert.* -import org.junit.Before import org.junit.Test class FindPlaceTest { - private lateinit var d : Diana - - @Before - fun initDiana(){ - d = Diana.Builder("YOUR_API_KEY").build() - } - @Test fun findPlaceBuiltSuccessfully() { - val findPlace = FindPlace.Builder(d) + val findPlace = FindPlace.Builder() .setInput("input") .setInputType(FindPlace.InputType.TEXTQUERY) .build() @@ -31,7 +22,7 @@ class FindPlaceTest { @Test fun findPlaceBuildFailedInputNotProvided() { assertThrows(UninitializedPropertyAccessException::class.java) { - FindPlace.Builder(d) + FindPlace.Builder() .setInputType(FindPlace.InputType.TEXTQUERY) .build() } @@ -40,7 +31,7 @@ class FindPlaceTest { @Test fun findPlaceBuildFailedInputTypeNotProvided() { assertThrows(UninitializedPropertyAccessException::class.java) { - FindPlace.Builder(d) + FindPlace.Builder() .setInput("input") .build() } @@ -49,14 +40,14 @@ class FindPlaceTest { @Test fun findPlaceBuildFailed() { assertThrows(UninitializedPropertyAccessException::class.java) { - FindPlace.Builder(d) + FindPlace.Builder() .build() } } @Test fun settersAndGettersRequiredFields() { - val findPlaceBuilder = FindPlace.Builder(d) + val findPlaceBuilder = FindPlace.Builder() .setInput("input") .setInputType(FindPlace.InputType.TEXTQUERY) @@ -72,7 +63,7 @@ class FindPlaceTest { @Test fun settersGettersLanguage(){ - val findPlaceBuilder = FindPlace.Builder(d) + val findPlaceBuilder = FindPlace.Builder() .setInput("input") .setInputType(FindPlace.InputType.TEXTQUERY) .setLanguage("en") @@ -100,7 +91,7 @@ class FindPlaceTest { Place.Field.WEBSITE_URI, Place.Field.RATING ) - val findPlaceBuilder = FindPlace.Builder(d) + val findPlaceBuilder = FindPlace.Builder() .setInput("input") .setInputType(FindPlace.InputType.TEXTQUERY) .setFields(fieldsAsPlaceField) @@ -141,7 +132,7 @@ class FindPlaceTest { FindPlace.Field.OPEN_NOW, FindPlace.Field.RATING ) - val findPlaceBuilder = FindPlace.Builder(d) + val findPlaceBuilder = FindPlace.Builder() .setInput("input") .setInputType(FindPlace.InputType.TEXTQUERY) .setFields(fieldsAsFindPlaceField) @@ -179,7 +170,7 @@ class FindPlaceTest { val lng :Double = 45.12 val radius :Double = 350.0 - val findPlaceBuilder = FindPlace.Builder(d) + val findPlaceBuilder = FindPlace.Builder() .setInput("input") .setInputType(FindPlace.InputType.TEXTQUERY) diff --git a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/NearbySearchTest.kt b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/NearbySearchTest.kt index 72edd5c..9becd92 100644 --- a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/NearbySearchTest.kt +++ b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/NearbySearchTest.kt @@ -1,99 +1,89 @@ package com.github.urmichm.placesearchktx.placesearch -import com.github.urmichm.placesearchktx.Diana import com.google.android.gms.maps.model.LatLng import org.junit.Assert.* import org.junit.Test +import kotlin.IllegalArgumentException class NearbySearchTest{ @Test fun minPriceIsOutOfRange(){ - val nearbySearch = NearbySearch.Builder(Diana.Builder("").build()) + val nearbySearch = NearbySearch.Builder() .setMinPrice(6) .setLocation(LatLng(0.0,0.0)) - .build() - val m = nearbySearch.validate() - assertFalse(m.isValid) - assertEquals("minPrice is out of possible range.", m.message) + assertThrows("minPrice is out of possible range.", IllegalArgumentException::class.java) { + nearbySearch.build() + } } @Test fun maxPriceIsOutOfRange(){ - val nearbySearch = NearbySearch.Builder(Diana.Builder("").build()) + val nearbySearch = NearbySearch.Builder() .setMinPrice(2) .setMaxPrice(5) .setLocation(LatLng(0.0,0.0)) - .build() - val m = nearbySearch.validate() - assertFalse(m.isValid) - assertEquals("maxPrice is out of possible range.", m.message) + assertThrows("maxPrice is out of possible range.", IllegalArgumentException::class.java) { + nearbySearch.build() + } } @Test fun radiusParameterIsOmittedWhenRankedByProminence(){ - val nearbySearch = NearbySearch.Builder(Diana.Builder("").build()) + val nearbySearch = NearbySearch.Builder() .setMinPrice(2) .setMaxPrice(4) .setLocation(LatLng(0.0,0.0)) - .build() - val m = nearbySearch.validate() - assertFalse(m.isValid) - assertEquals("When prominence is specified, the radius parameter is required.", m.message) + assertThrows("When prominence is specified, the radius parameter is required.", IllegalArgumentException::class.java) { + nearbySearch.build() + } } @Test fun radiusParameterIsGivenWhenRankedByDistance(){ - val nearbySearch = NearbySearch.Builder(Diana.Builder("").build()) + val nearbySearch = NearbySearch.Builder() .setRankBy(NearbySearch.Rankby.DISTANCE) .setRadius(5000) .setMinPrice(2) .setMaxPrice(4) .setLocation(LatLng(0.0,0.0)) - .build() - val m = nearbySearch.validate() - assertFalse(m.isValid) - assertEquals("When using rankBy=distance, the radius parameter will not be accepted, and will result in an INVALID_REQUEST.", m.message) + assertThrows("When using rankBy=distance, the radius parameter will not be accepted, and will result in an INVALID_REQUEST.", IllegalArgumentException::class.java) { + nearbySearch.build() + } } @Test fun validWhenRankedByDistanceAndRadiusOmitted(){ - val nearbySearch = NearbySearch.Builder(Diana.Builder("").build()) + val nearbySearch = NearbySearch.Builder() .setRankBy(NearbySearch.Rankby.DISTANCE) .setMinPrice(2) .setMaxPrice(4) .setLocation(LatLng(0.0,0.0)) .build() - val m = nearbySearch.validate() - assertTrue(m.isValid) - assertEquals("OK", m.message) + assertNotNull(nearbySearch) } @Test fun validWhenRankedByProminenceAndRadiusGiven(){ - val nearbySearch = NearbySearch.Builder(Diana.Builder("").build()) + val nearbySearch = NearbySearch.Builder() .setRadius(1000) .setMinPrice(2) .setMaxPrice(4) .setLocation(LatLng(0.0,0.0)) .build() - val m = nearbySearch.validate() - assertTrue(m.isValid) - assertEquals("OK", m.message) + assertNotNull(nearbySearch) } @Test fun locationNotProvided() { - val diana = Diana.Builder("").build() - - val builder = NearbySearch.Builder(diana) + val builder = NearbySearch.Builder() assertThrows(Exception::class.java) { builder.build() diff --git a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/TextSearchTest.kt b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/TextSearchTest.kt index 4b9529e..3d9c8e6 100644 --- a/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/TextSearchTest.kt +++ b/placesearch-ktx/src/test/java/com/github/urmichm/placesearchktx/placesearch/TextSearchTest.kt @@ -1,33 +1,58 @@ package com.github.urmichm.placesearchktx.placesearch -import com.github.urmichm.placesearchktx.Diana import org.junit.Assert.* -import org.junit.Before import org.junit.Test class TextSearchTest { - private lateinit var d : Diana - - @Before - fun initDiana(){ - d = Diana.Builder("YOUR_API_KEY").build() - } @Test fun queryParamNotSpecified(){ assertThrows(UninitializedPropertyAccessException::class.java) { - TextSearch.Builder(d) + TextSearch.Builder() .build() } } @Test fun queryParamSpecified(){ - val textSearch = TextSearch.Builder(d) + val textSearch = TextSearch.Builder() .setQuery("query") .build() assertNotNull(textSearch) } + @Test + fun maxPriceOutOfRange(){ + val textSearch = TextSearch.Builder() + .setQuery("query") + .setMaxPrice(6) + + assertThrows("maxPrice is out of possible range.", IllegalArgumentException::class.java) { + textSearch.build() + } + } + + @Test + fun minPriceOutOfRange(){ + val textSearch = TextSearch.Builder() + .setQuery("query") + .setMinPrice(-1) + + assertThrows("minPrice is out of possible range.", IllegalArgumentException::class.java) { + textSearch.build() + } + } + + @Test + fun regionIsIncorrect(){ + val textSearch = TextSearch.Builder() + .setQuery("query") + .setRegion("pll") + + assertThrows("The region code, must be specified as a ccTLD (\"top-level domain\") TWO-character value.", IllegalArgumentException::class.java) { + textSearch.build() + } + } + } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7a41e71..cfbf008 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,10 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories {