├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ └── activity_example_location_search.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── opensource │ │ │ └── oneclickaway │ │ │ └── android │ │ │ └── searchplaces │ │ │ └── ExampleLocationSearch.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── opensource │ │ │ └── oneclickaway │ │ │ └── android │ │ │ └── searchplaces │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── opensource │ │ └── oneclickaway │ │ └── android │ │ └── searchplaces │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── place_autocomplete ├── .gitignore ├── TestLab.zip ├── src │ ├── main │ │ ├── res │ │ │ ├── raw │ │ │ │ └── loading_spinner.gif │ │ │ ├── drawable │ │ │ │ ├── ic_arrow_back_black_24dp.xml │ │ │ │ ├── ic_close_black_24dp.xml │ │ │ │ ├── ic_location_on_black_24dp.xml │ │ │ │ ├── ic_access_time_black_24dp.xml │ │ │ │ └── ic_search_black_24dp.xml │ │ │ ├── layout │ │ │ │ ├── date_item_row.xml │ │ │ │ ├── loading_place_details.xml │ │ │ │ ├── search_result_row.xml │ │ │ │ ├── recent_search_result_row.xml │ │ │ │ └── activity_search_place.xml │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── oneclickaway │ │ │ │ └── opensource │ │ │ │ └── placeautocomplete │ │ │ │ ├── utils │ │ │ │ ├── LoadingManager.java │ │ │ │ ├── SearchPlacesStatusCodes.kt │ │ │ │ ├── Commons.kt │ │ │ │ └── GroupStrategy.kt │ │ │ │ ├── data │ │ │ │ ├── api │ │ │ │ │ ├── bean │ │ │ │ │ │ ├── places_response │ │ │ │ │ │ │ ├── TermsItem.kt │ │ │ │ │ │ │ ├── MainTextMatchedSubstringsItem.kt │ │ │ │ │ │ │ ├── MatchedSubstringsItem.kt │ │ │ │ │ │ │ ├── SearchResponse.kt │ │ │ │ │ │ │ ├── StructuredFormatting.kt │ │ │ │ │ │ │ └── PredictionsItem.kt │ │ │ │ │ │ └── place_details │ │ │ │ │ │ │ ├── Close.kt │ │ │ │ │ │ │ ├── Open.kt │ │ │ │ │ │ │ ├── Location.kt │ │ │ │ │ │ │ ├── Northeast.kt │ │ │ │ │ │ │ ├── PeriodsItem.kt │ │ │ │ │ │ │ ├── Geometry.kt │ │ │ │ │ │ │ ├── PlacesDetailsResponse.kt │ │ │ │ │ │ │ ├── Southwest.kt │ │ │ │ │ │ │ ├── PlusCode.kt │ │ │ │ │ │ │ ├── OpeningHours.kt │ │ │ │ │ │ │ ├── Viewport.kt │ │ │ │ │ │ │ ├── AddressComponentsItem.kt │ │ │ │ │ │ │ ├── PhotosItem.kt │ │ │ │ │ │ │ ├── ReviewsItem.kt │ │ │ │ │ │ │ └── PlaceDetails.kt │ │ │ │ │ └── base │ │ │ │ │ │ ├── SearchPlaceApi.kt │ │ │ │ │ │ └── RESTAPIManager.kt │ │ │ │ ├── model │ │ │ │ │ ├── room │ │ │ │ │ │ └── SearchSelectedItem.kt │ │ │ │ │ └── view │ │ │ │ │ │ └── SearchPlacesViewModel.kt │ │ │ │ ├── room │ │ │ │ │ ├── RecentSearchesDAO.kt │ │ │ │ │ └── RecentSearchesDB.kt │ │ │ │ ├── adapter │ │ │ │ │ ├── SearchResultAdapter.kt │ │ │ │ │ └── RecentSearchesAdapter.kt │ │ │ │ └── repositories │ │ │ │ │ └── SearchPlacesRepo.kt │ │ │ │ ├── interfaces │ │ │ │ └── SearchPlaces.kt │ │ │ │ └── ui │ │ │ │ └── SearchPlaceActivity.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── oneclickaway │ │ │ └── opensource │ │ │ └── placeautocomplete │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── oneclickaway │ │ └── opensource │ │ └── placeautocomplete │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── dry_run.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── encodings.xml ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── dictionaries │ └── default.xml ├── vcs.xml ├── misc.xml ├── runConfigurations.xml └── gradle.xml ├── .gitignore ├── gradle.properties ├── LICENSE ├── gradlew.bat ├── gradlew └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /place_autocomplete/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':place_autocomplete' 2 | -------------------------------------------------------------------------------- /dry_run.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/dry_run.gif -------------------------------------------------------------------------------- /place_autocomplete/TestLab.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/place_autocomplete/TestLab.zip -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/raw/loading_spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drabu/PlaceAutocomplete/HEAD/place_autocomplete/src/main/res/raw/loading_spinner.gif -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/dictionaries/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | okhttp 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri May 03 14:55:57 IST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/utils/LoadingManager.java: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.utils; 2 | 3 | public enum LoadingManager { 4 | STATE_REFRESHING, 5 | STATE_COMPLETED, 6 | STATE_ERROR, 7 | STATE_NO_INTERNET, 8 | STATE_NO_RESULT, 9 | STATE_IDLE 10 | } 11 | 12 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/drawable/ic_arrow_back_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SearchPlaces 3 | Enter location name 4 | Sample Place 5 | Sample place formatted address 6 | Search for a landmark or locality 7 | Enter some location 8 | 9 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/drawable/ic_close_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/places_response/TermsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** @author @buren ---> {Google response for predicted places}*/ 6 | data class TermsItem( 7 | 8 | @field:SerializedName("offset") 9 | val offset: Int? = null, 10 | 11 | @field:SerializedName("value") 12 | val value: String? = null 13 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/places_response/MainTextMatchedSubstringsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response 2 | 3 | import com.google.gson.annotations.SerializedName 4 | /** @author @buren ---> {Google response for predicted places}*/ 5 | data class MainTextMatchedSubstringsItem( 6 | 7 | @field:SerializedName("offset") 8 | val offset: Int? = null, 9 | 10 | @field:SerializedName("length") 11 | val length: Int? = null 12 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/places_response/MatchedSubstringsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** @author @buren ---> {Google response for predicted places}*/ 6 | data class MatchedSubstringsItem( 7 | 8 | @field:SerializedName("offset") 9 | val offset: Int? = null, 10 | 11 | @field:SerializedName("length") 12 | val length: Int? = null 13 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/places_response/SearchResponse.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** @author @buren ---> {Google response for predicted places}*/ 6 | data class SearchResponse( 7 | 8 | @field:SerializedName("predictions") 9 | val predictions: List? = null, 10 | 11 | @field:SerializedName("status") 12 | val status: String? = null 13 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/drawable/ic_location_on_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /place_autocomplete/src/test/java/com/oneclickaway/opensource/placeautocomplete/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/model/room/SearchSelectedItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.model.room 2 | 3 | import android.arch.persistence.room.Entity 4 | import android.arch.persistence.room.PrimaryKey 5 | 6 | /** 7 | *@author Burhan ud din ---> item table 8 | */ 9 | @Entity(tableName = "SearchSelectedItem") 10 | data class SearchSelectedItem( 11 | @PrimaryKey var placeId: String, 12 | var mainText: String, 13 | var secondaryText: String, 14 | var searchCurrentMilliseconds: Long 15 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/drawable/ic_access_time_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/opensource/oneclickaway/android/searchplaces/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.opensource.oneclickaway.android.searchplaces 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | /** 13 | *@author Burhan ud din ---> test case 14 | */ 15 | @Test 16 | fun addition_isCorrect() { 17 | assertEquals(4, 2 + 2) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Close.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | /** @author @buren ---> {Google response for place details}*/ 8 | @Parcelize 9 | data class Close( 10 | 11 | @field:SerializedName("time") 12 | var time: String? = null, 13 | 14 | @field:SerializedName("day") 15 | var day: Int 16 | 17 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/drawable/ic_search_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Open.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class Open( 12 | 13 | @field:SerializedName("time") 14 | var time: String? = null, 15 | 16 | @field:SerializedName("day") 17 | var day: Int? = null 18 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Location.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class Location( 12 | 13 | @field:SerializedName("lng") 14 | var lng: Double? = null, 15 | 16 | @field:SerializedName("lat") 17 | var lat: Double? = null 18 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Northeast.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class Northeast( 12 | 13 | @field:SerializedName("lng") 14 | var lng: Double? = null, 15 | 16 | @field:SerializedName("lat") 17 | var lat: Double? = null 18 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/places_response/StructuredFormatting.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** @author @buren ---> {Google response for predicted places}*/ 6 | data class StructuredFormatting( 7 | 8 | @field:SerializedName("main_text_matched_substrings") 9 | val mainTextMatchedSubstrings: List? = null, 10 | 11 | @field:SerializedName("secondary_text") 12 | val secondaryText: String? = null, 13 | 14 | @field:SerializedName("main_text") 15 | val mainText: String? = null 16 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/PeriodsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class PeriodsItem( 12 | 13 | @field:SerializedName("close") 14 | var close: Close? = null, 15 | 16 | @field:SerializedName("open") 17 | var open: Open? = null 18 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Geometry.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class Geometry( 12 | 13 | @field:SerializedName("viewport") 14 | var viewport: Viewport? = null, 15 | 16 | @field:SerializedName("location") 17 | var location: Location? = null 18 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/PlacesDetailsResponse.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import javax.annotation.Generated 5 | 6 | /** @author @buren ---> {Google response for place details}*/ 7 | @Generated("com.robohorse.robopojogenerator") 8 | data class PlacesDetailsResponse( 9 | 10 | @field:SerializedName("result") 11 | var result: PlaceDetails? = null, 12 | 13 | @field:SerializedName("html_attributions") 14 | var htmlAttributions: List? = null, 15 | 16 | @field:SerializedName("status") 17 | var status: String? = null 18 | ) -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/layout/date_item_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/layout/loading_place_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Southwest.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.arch.persistence.room.Entity 4 | import android.os.Parcelable 5 | import com.google.gson.annotations.SerializedName 6 | import kotlinx.android.parcel.Parcelize 7 | import javax.annotation.Generated 8 | 9 | /** @author @buren ---> {Google response for place details}*/ 10 | @Entity(tableName = "Southwest") 11 | @Parcelize 12 | @Generated("com.robohorse.robopojogenerator") 13 | data class Southwest( 14 | 15 | @field:SerializedName("lng") 16 | var lng: Double? = null, 17 | 18 | @field:SerializedName("lat") 19 | var lat: Double? = null 20 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/PlusCode.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.arch.persistence.room.Entity 4 | import android.os.Parcelable 5 | import com.google.gson.annotations.SerializedName 6 | import kotlinx.android.parcel.Parcelize 7 | import javax.annotation.Generated 8 | 9 | /** @author @buren ---> {Google response for place details}*/ 10 | @Entity(tableName = "PlusCode") 11 | @Parcelize 12 | @Generated("com.robohorse.robopojogenerator") 13 | data class PlusCode( 14 | @field:SerializedName("compound_code") 15 | var compoundCode: String? = null, 16 | 17 | @field:SerializedName("global_code") 18 | var globalCode: String? = null 19 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/OpeningHours.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class OpeningHours( 12 | 13 | @field:SerializedName("open_now") 14 | var openNow: Boolean? = null, 15 | 16 | @field:SerializedName("periods") 17 | var periods: List? = null, 18 | 19 | @field:SerializedName("weekday_text") 20 | var weekdayText: List? = null 21 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/Viewport.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.arch.persistence.room.Entity 4 | import android.arch.persistence.room.PrimaryKey 5 | import android.os.Parcelable 6 | import com.google.gson.annotations.SerializedName 7 | import kotlinx.android.parcel.Parcelize 8 | import javax.annotation.Generated 9 | 10 | /** @author @buren ---> {Google response for place details}*/ 11 | @Entity(tableName = "Viewport") 12 | @Parcelize 13 | @Generated("com.robohorse.robopojogenerator") 14 | data class Viewport( 15 | 16 | @PrimaryKey 17 | @field:SerializedName("southwest") 18 | val southwest: Southwest? = null, 19 | 20 | @field:SerializedName("northeast") 21 | val northeast: Northeast? = null 22 | 23 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/AddressComponentsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.arch.persistence.room.PrimaryKey 4 | import android.os.Parcelable 5 | import com.google.gson.annotations.SerializedName 6 | import kotlinx.android.parcel.Parcelize 7 | import javax.annotation.Generated 8 | 9 | /** @author @buren ---> {Google response for place details}*/ 10 | @Parcelize 11 | @Generated("com.robohorse.robopojogenerator") 12 | class AddressComponentsItem( 13 | 14 | @field:SerializedName("types") 15 | var types: List? = null, 16 | 17 | @field:SerializedName("short_name") 18 | var shortName: String? = null, 19 | 20 | @field:SerializedName("long_name") 21 | @PrimaryKey var longName: String 22 | 23 | ) : Parcelable -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -keep class org.ocpsoft.prettytime.i18n.** -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # Kotlin code style for this project: "official" or "obsolete": 15 | kotlin.code.style=official 16 | android.debug.obsoleteApi=true -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/PhotosItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class PhotosItem( 12 | 13 | @field:SerializedName("photo_reference") 14 | var photoReference: String? = null, 15 | 16 | @field:SerializedName("width") 17 | var width: Int? = null, 18 | 19 | @field:SerializedName("html_attributions") 20 | var htmlAttributions: List? = null, 21 | 22 | @field:SerializedName("height") 23 | var height: Int? = null 24 | 25 | ) : Parcelable -------------------------------------------------------------------------------- /app/src/androidTest/java/com/opensource/oneclickaway/android/searchplaces/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.opensource.oneclickaway.android.searchplaces 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | /** @author @buren ---> {sample test case}*/ 19 | @Test 20 | fun useAppContext() { 21 | // Context of the app under test. 22 | val appContext = InstrumentationRegistry.getTargetContext() 23 | assertEquals("com.uipep.android.searchplaces", appContext.packageName) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/interfaces/SearchPlaces.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.interfaces 2 | 3 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response.PredictionsItem 4 | import com.oneclickaway.opensource.placeautocomplete.utils.GroupStrategy 5 | 6 | /** @author @buren ---> {place click listener used to get clicked item}*/ 7 | interface SearchPlaces { 8 | 9 | /** 10 | *@author Burhan ud din ---> Place selected 11 | */ 12 | interface PlaceItemSelectedListener { 13 | /** @author @buren ---> {place clicked}*/ 14 | fun onPlaceItemSelected(candidateItem: PredictionsItem?) 15 | 16 | } 17 | 18 | /** 19 | *@author Burhan ud din ---> place selected in recent 20 | */ 21 | interface RecentItemSelectedListener { 22 | /** 23 | *@author Burhan ud din ---> recent items 24 | */ 25 | fun onRecantsItemSelected(savedItem: GroupStrategy.ListItem) 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /place_autocomplete/src/androidTest/java/com/oneclickaway/opensource/placeautocomplete/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | 20 | /** @author @buren ---> {sample test case}*/ 21 | @Test 22 | public void useAppContext() { 23 | // Context of the app under test. 24 | Context appContext = InstrumentationRegistry.getTargetContext(); 25 | 26 | assertEquals("com.oneclickaway.opensource.placeautocomplete.test", appContext.getPackageName()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/room/RecentSearchesDAO.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.room 2 | 3 | import android.arch.persistence.room.Dao 4 | import android.arch.persistence.room.Insert 5 | import android.arch.persistence.room.OnConflictStrategy 6 | import android.arch.persistence.room.Query 7 | import com.oneclickaway.opensource.placeautocomplete.data.model.room.SearchSelectedItem 8 | 9 | /** 10 | *@author Burhan ud din ---> Data access Object 11 | */ 12 | @Dao 13 | interface RecentSearchesDAO { 14 | 15 | /** 16 | *@author Burhan ud din ---> method used to get recent searches list 17 | */ 18 | @Query("SELECT * FROM SearchSelectedItem ORDER BY searchCurrentMilliseconds DESC") 19 | fun getRecentSearches(): List 20 | 21 | /** 22 | *@author Burhan ud din ---> method used to add item searched 23 | */ 24 | @Insert(onConflict = OnConflictStrategy.REPLACE) 25 | fun addSearchItem(searchSelectedItem: SearchSelectedItem) 26 | 27 | } -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/places_response/PredictionsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** @author @buren ---> {Google response for predicted places}*/ 6 | data class PredictionsItem( 7 | 8 | @field:SerializedName("reference") 9 | val reference: String? = null, 10 | 11 | @field:SerializedName("types") 12 | val types: List? = null, 13 | 14 | @field:SerializedName("matched_substrings") 15 | val matchedSubstrings: List? = null, 16 | 17 | @field:SerializedName("terms") 18 | val terms: List? = null, 19 | 20 | @field:SerializedName("structured_formatting") 21 | val structuredFormatting: StructuredFormatting? = null, 22 | 23 | @field:SerializedName("description") 24 | val description: String? = null, 25 | 26 | @field:SerializedName("id") 27 | val id: String? = null, 28 | 29 | @field:SerializedName("place_id") 30 | val placeId: String? = null 31 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Burhan ud din 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/base/SearchPlaceApi.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.base 2 | 3 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details.PlacesDetailsResponse 4 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response.SearchResponse 5 | import io.reactivex.Observable 6 | import retrofit2.http.GET 7 | import retrofit2.http.Query 8 | /** @author @buren ---> {api documentation}*/ 9 | interface SearchPlaceApi { 10 | 11 | /** @author @buren ---> {return the details of a specific place}*/ 12 | @GET("details/json") 13 | fun getPlaceDetailsFromPlaceId( 14 | @Query("placeid") placeId: String, @Query("key") apiKey: String 15 | ): Observable 16 | 17 | /** @author @buren ---> {gets the list of predictions}*/ 18 | @GET("autocomplete/json") 19 | fun getPlaceResults( 20 | @Query("input") placeHint: String, @Query("key") apiKey: String, @Query("location") location: String, @Query( 21 | "radius" 22 | ) radius: String 23 | ): Observable 24 | 25 | } 26 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/utils/SearchPlacesStatusCodes.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.utils 2 | /** @author @buren ---> {file contains library constants}*/ 3 | object SearchPlacesStatusCodes { 4 | /** @author @buren ---> {Use this constant to set title for search places activity}*/ 5 | val SEARCH_TITLE: String? = "searchTitle" 6 | 7 | /** @author @buren ---> {Use this constant to set radius of search from your current position}*/ 8 | val ENCLOSE_RADIUS: String? ="enclosingRadius" 9 | 10 | /** @author @buren ---> {Use this constant to search places from your current location}*/ 11 | val CURRENT_LOCATION: String? = "location" 12 | 13 | /** @author @buren ---> {This is mandatory field which is required to make places search}*/ 14 | val GOOGLE_API_KEY: String? = "apiKey" 15 | 16 | /** @author @buren ---> {This code hold the details of place returned}*/ 17 | val PLACE_DATA: String? = "PLACE_DETAILS" 18 | 19 | /** @author @buren ---> {Used to check if data is in correct form}*/ 20 | val GOOGLE_SEARCH_RESULT_OK = "OK" 21 | 22 | val PLACEHOLDER_TRANSITION = "searchPlaceHolder" 23 | 24 | val CONFIG = "configuration" 25 | 26 | 27 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_example_location_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 26 | 27 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/ReviewsItem.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.arch.persistence.room.Entity 4 | import android.os.Parcelable 5 | import com.google.gson.annotations.SerializedName 6 | import kotlinx.android.parcel.Parcelize 7 | import javax.annotation.Generated 8 | 9 | /** @author @buren ---> {Google response for place details}*/ 10 | 11 | @Entity(tableName = "ReviewsItem") 12 | @Parcelize 13 | @Generated("com.robohorse.robopojogenerator") 14 | data class ReviewsItem( 15 | 16 | @field:SerializedName("author_name") 17 | var authorName: String? = null, 18 | 19 | @field:SerializedName("profile_photo_url") 20 | var profilePhotoUrl: String? = null, 21 | 22 | @field:SerializedName("author_url") 23 | var authorUrl: String? = null, 24 | 25 | @field:SerializedName("rating") 26 | var rating: Int? = null, 27 | 28 | @field:SerializedName("language") 29 | var language: String? = null, 30 | 31 | @field:SerializedName("text") 32 | var text: String? = null, 33 | 34 | @field:SerializedName("time") 35 | var time: Int? = null, 36 | 37 | @field:SerializedName("relative_time_description") 38 | var relativeTimeDescription: String? = null 39 | 40 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PlaceAutocomplete 3 | 4 | Enter location name 5 | Sample Place 6 | Sample place formatted address 7 | Search for a landmark or locality 8 | TODO 9 | Enter location 10 | searchPlaceHolder 11 | Enter some location 12 | Location not found 13 | Please check spell errors.. 14 | 15 | 16 | hour ago 17 | hours ago 18 | minutes ago 19 | days ago 20 | week ago 21 | weeks ago 22 | mon ago 23 | year ago 24 | No Interent. Please check your internet connection and try again. 25 | We are having trouble connecting to our servers. 26 | 27 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/base/RESTAPIManager.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.base 2 | 3 | import okhttp3.OkHttpClient 4 | import okhttp3.logging.HttpLoggingInterceptor 5 | import retrofit2.Retrofit 6 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 7 | import retrofit2.converter.gson.GsonConverterFactory 8 | import java.util.concurrent.TimeUnit 9 | 10 | /** @author @buren ---> {Api manager for REST Api}*/ 11 | object RESTAPIManager { 12 | 13 | private var BASE_URL = "https://maps.googleapis.com/maps/api/place/" 14 | 15 | private fun getOkhttpClient(): OkHttpClient { 16 | 17 | val okhttpBuilder = OkHttpClient.Builder() 18 | okhttpBuilder.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) 19 | okhttpBuilder.callTimeout(100, TimeUnit.SECONDS) 20 | okhttpBuilder.retryOnConnectionFailure(true) 21 | 22 | return okhttpBuilder.build() 23 | } 24 | 25 | /** @author @buren ---> {returns the instance of retrofit for making api calls}*/ 26 | fun getInstance(): SearchPlaceApi { 27 | val retroFit = Retrofit.Builder().client(getOkhttpClient()).baseUrl(BASE_URL) 28 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 29 | .addConverterFactory(GsonConverterFactory.create()).build() 30 | return retroFit.create(SearchPlaceApi::class.java) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/room/RecentSearchesDB.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.room 2 | 3 | import android.arch.persistence.room.Database 4 | import android.arch.persistence.room.Room 5 | import android.arch.persistence.room.RoomDatabase 6 | import android.content.Context 7 | import com.oneclickaway.opensource.placeautocomplete.data.model.room.SearchSelectedItem 8 | 9 | /** @author Burhan ud din ---> Database class for local storage*/ 10 | @Database( 11 | entities = [ 12 | SearchSelectedItem::class 13 | ], version = 1, exportSchema = false 14 | ) 15 | abstract class RecentSearchesDB : RoomDatabase() { 16 | 17 | /** @author Burhan ud din ---> Data Access Object with inbuilt implementation*/ 18 | abstract fun repDao(): RecentSearchesDAO 19 | 20 | companion object { 21 | 22 | var INSTANCE: RecentSearchesDB? = null 23 | 24 | /** 25 | *@author Burhan ud din ---> Gets the instance of database 26 | */ 27 | fun getInstance(context: Context): RecentSearchesDB? { 28 | 29 | if (INSTANCE == null) { 30 | INSTANCE = 31 | Room.databaseBuilder( 32 | context.applicationContext, 33 | RecentSearchesDB::class.java, 34 | "recent_searches" 35 | ) 36 | .build() 37 | 38 | } else { 39 | INSTANCE 40 | } 41 | return INSTANCE 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 28 8 | 9 | defaultConfig { 10 | applicationId "com.opensource.oneclickaway.android.searchplaces" 11 | minSdkVersion 15 12 | 13 | targetSdkVersion 28 14 | versionCode 1 15 | versionName "1.0" 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | 20 | debug { 21 | buildConfigField 'String', "ApiKey", SearchPlaces_MapsApiKey 22 | } 23 | 24 | release { 25 | buildConfigField 'String', "ApiKey", SearchPlaces_MapsApiKey 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(dir: 'libs', include: ['*.jar']) 41 | implementation 'com.android.support:appcompat-v7:28.0.0' 42 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 43 | testImplementation 'junit:junit:4.12' 44 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 45 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 46 | 47 | implementation project(':place_autocomplete') 48 | 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/utils/Commons.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.utils 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import java.util.* 6 | 7 | /** 8 | *@author Burhan ud din ---> Common utilities 9 | */ 10 | object Commons { 11 | 12 | /** 13 | *@author Burhan ud din ---> tell you if the user is connected to the network 14 | */ 15 | fun isNetworkConnected(mContext: Context): Boolean { 16 | val cm = mContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager? 17 | return cm!!.activeNetworkInfo != null 18 | } 19 | 20 | /** 21 | *@author Burhan ud din ---> Used to get pretty time from milliseconds 22 | */ 23 | fun getPrettyTime(milliSeconds: Long): String { 24 | val default = 0 25 | 26 | val minutesToYesterday = 1440 27 | val minutesToWeek = 10080 28 | val minutesToPost = ((System.currentTimeMillis() - milliSeconds) / 1000F) / 60F 29 | 30 | val currentDateInstance = Calendar.getInstance() 31 | currentDateInstance.set( 32 | currentDateInstance.get(Calendar.YEAR), 33 | currentDateInstance.get(Calendar.DAY_OF_MONTH), 34 | currentDateInstance.get(Calendar.DATE) 35 | ) 36 | 37 | 38 | val currentTimeCalender = Calendar.getInstance() 39 | currentTimeCalender.set( 40 | currentTimeCalender.get(Calendar.YEAR), 41 | currentTimeCalender.get(Calendar.DAY_OF_MONTH), 42 | currentTimeCalender.get(Calendar.DATE), 43 | default, default, default 44 | ) 45 | 46 | val minutesElapsedForToday = 47 | ((currentDateInstance.timeInMillis - currentTimeCalender.timeInMillis) / 1000F) / 60F 48 | 49 | return when { 50 | minutesToPost < minutesElapsedForToday -> "Today" 51 | (minutesToPost - minutesElapsedForToday) < minutesToYesterday -> "Yesterday" 52 | (minutesToPost - minutesElapsedForToday) < minutesToWeek -> "Earlier this week" 53 | else -> "Previous Searches" 54 | } 55 | 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/layout/search_result_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 29 | 30 | 39 | 40 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/layout/recent_search_result_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 30 | 31 | 40 | 41 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/api/bean/place_details/PlaceDetails.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | import javax.annotation.Generated 7 | 8 | /** @author @buren ---> {Google response for place details}*/ 9 | @Parcelize 10 | @Generated("com.robohorse.robopojogenerator") 11 | data class PlaceDetails ( 12 | 13 | @field:SerializedName("utc_offset") 14 | var utcOffset: Int? = null, 15 | 16 | @field:SerializedName("formatted_address") 17 | var formattedAddress: String? = null, 18 | 19 | @field:SerializedName("types") 20 | var types: List? = null, 21 | 22 | @field:SerializedName("website") 23 | var website: String? = null, 24 | 25 | @field:SerializedName("icon") 26 | var icon: String? = null, 27 | 28 | @field:SerializedName("rating") 29 | var rating: Double? = null, 30 | 31 | @field:SerializedName("address_components") 32 | var addressComponents: List? = null, 33 | 34 | @field:SerializedName("photos") 35 | var photos: List? = null, 36 | 37 | @field:SerializedName("url") 38 | var url: String? = null, 39 | 40 | @field:SerializedName("reference") 41 | var reference: String? = null, 42 | 43 | @field:SerializedName("user_ratings_total") 44 | var userRatingsTotal: Int? = null, 45 | 46 | @field:SerializedName("reviews") 47 | var reviews: List? = null, 48 | 49 | @field:SerializedName("scope") 50 | var scope: String? = null, 51 | 52 | @field:SerializedName("name") 53 | var name: String? = null, 54 | 55 | @field:SerializedName("opening_hours") 56 | var openingHours: OpeningHours? = null, 57 | 58 | @field:SerializedName("geometry") 59 | var geometry: Geometry? = null, 60 | 61 | @field:SerializedName("vicinity") 62 | var vicinity: String? = null, 63 | 64 | @field:SerializedName("id") 65 | var id: String? = null, 66 | 67 | @field:SerializedName("adr_address") 68 | var adrAddress: String? = null, 69 | 70 | @field:SerializedName("plus_code") 71 | var plusCode: PlusCode? = null, 72 | 73 | @field:SerializedName("formatted_phone_number") 74 | var formattedPhoneNumber: String? = null, 75 | 76 | @field:SerializedName("international_phone_number") 77 | var internationalPhoneNumber: String? = null, 78 | 79 | @field:SerializedName("place_id") 80 | var placeId: String? = null 81 | ) : Parcelable -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/model/view/SearchPlacesViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.model.view 2 | 3 | import android.app.Application 4 | import android.arch.lifecycle.AndroidViewModel 5 | import android.arch.lifecycle.LiveData 6 | import com.oneclickaway.opensource.placeautocomplete.data.model.room.SearchSelectedItem 7 | import com.oneclickaway.opensource.placeautocomplete.data.repositories.SearchPlacesRepo 8 | import com.oneclickaway.opensource.placeautocomplete.utils.LoadingManager 9 | 10 | /** @author @buren ---> {view model used to hold data while device configuration is changed}*/ 11 | class SearchPlacesViewModel(private val applicationContext: Application) : 12 | AndroidViewModel(applicationContext) { 13 | 14 | 15 | private val searchPlacesRepo = SearchPlacesRepo(applicationContext) 16 | 17 | 18 | /** @author @buren ---> {micro-service used to request data stream of search results}*/ 19 | fun requestListOfSearchResults(placeHint: String, apiKey : String, location : String, radius : String) { 20 | searchPlacesRepo.requestListOfSearchResults(placeHint = placeHint, apiKey = apiKey, location = location, radius = radius ) 21 | } 22 | 23 | /** @author @buren ---> {micro-service used to get live data stream of search results}*/ 24 | fun getLiveListOfSearchResultsStream() = searchPlacesRepo.getLiveListOfSearchResultsStream() 25 | 26 | 27 | /** @author @buren ---> {micro-service used to request data stream of place details}*/ 28 | fun requestPlaceDetails(placeId : String, apiKey : String){ 29 | searchPlacesRepo.requestPlaceDetails(placeId = placeId, apiKey = apiKey) 30 | } 31 | 32 | /** @author @buren ---> {micro-service used to get live data stream of place details}*/ 33 | fun getPlaceDetailsLiveDataStream() = searchPlacesRepo.getPlaceDetailsLiveDataStream() 34 | 35 | /** @author @buren ---> {prediction manager}*/ 36 | fun getLoadingPredictionManager(): LiveData = 37 | searchPlacesRepo.getLoadingPredictionManager() 38 | 39 | /** @author @buren ---> {places Manager}*/ 40 | fun getLoadingPlaceManager(): LiveData = searchPlacesRepo.getLoadingPlaceManager() 41 | 42 | /** @author @buren ---> {Searches manager}*/ 43 | fun getRecentSearchesManager(): LiveData = 44 | searchPlacesRepo.getRecentSearchesManager() 45 | 46 | /** @author @buren ---> {Search data}*/ 47 | fun getRecentSearchesData(): LiveData> = 48 | searchPlacesRepo.getRecentSearches() 49 | 50 | /** @author @buren ---> {recent searches}*/ 51 | fun requestListOfRecentSearches() = 52 | searchPlacesRepo.requestListOfRecentSearches(applicationContext) 53 | 54 | 55 | } -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/adapter/SearchResultAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.adapter 2 | 3 | import android.annotation.SuppressLint 4 | import android.support.v7.widget.RecyclerView 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | import com.jakewharton.rxbinding2.view.RxView 10 | import com.oneclickaway.opensource.placeautocomplete.R 11 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response.PredictionsItem 12 | import com.oneclickaway.opensource.placeautocomplete.interfaces.SearchPlaces 13 | import java.util.concurrent.TimeUnit 14 | 15 | /** @author @buren ---> {adapter to set result views in row}*/ 16 | class SearchResultAdapter( 17 | var listOfCandidatesItem: List? = ArrayList(), 18 | var placeItemSelectedListener: SearchPlaces.PlaceItemSelectedListener 19 | ) : 20 | RecyclerView.Adapter() { 21 | 22 | override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ViewHolder = 23 | ViewHolder( 24 | binding = LayoutInflater.from(p0.context).inflate(R.layout.search_result_row, p0, false) 25 | ) 26 | 27 | override fun getItemCount(): Int = listOfCandidatesItem?.size!! 28 | 29 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 30 | 31 | holder.placeTitleTV.text = listOfCandidatesItem?.get(position)?.structuredFormatting?.mainText 32 | holder.placeFormattedAddressTV.text = 33 | listOfCandidatesItem?.get(position)?.structuredFormatting?.secondaryText 34 | 35 | } 36 | 37 | 38 | /** @author @buren ---> {sets items in view}*/ 39 | fun setSearchCandidates(listOfCandidatesItem: List?) { 40 | this.listOfCandidatesItem = listOfCandidatesItem 41 | notifyDataSetChanged() 42 | } 43 | 44 | 45 | /** @author @buren ---> {holds row vew}*/ 46 | inner class ViewHolder( 47 | var binding: View 48 | ) : RecyclerView.ViewHolder(binding) { 49 | 50 | lateinit var placeTitleTV: TextView 51 | lateinit var placeFormattedAddressTV: TextView 52 | 53 | init { 54 | 55 | inflateViews() 56 | 57 | setItemClickListener() 58 | } 59 | 60 | private fun inflateViews() { 61 | 62 | placeTitleTV = itemView.findViewById(R.id.placeTitleTV) 63 | placeFormattedAddressTV = itemView.findViewById(R.id.placeFormattedAddressTV) 64 | 65 | 66 | } 67 | 68 | @SuppressLint("CheckResult") 69 | private fun setItemClickListener() { 70 | RxView.clicks(binding) 71 | .throttleFirst(700, TimeUnit.MILLISECONDS) 72 | .subscribe { 73 | placeItemSelectedListener.onPlaceItemSelected( 74 | listOfCandidatesItem?.get( 75 | adapterPosition 76 | ) 77 | ) 78 | } 79 | } 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /app/src/main/java/com/opensource/oneclickaway/android/searchplaces/ExampleLocationSearch.kt: -------------------------------------------------------------------------------- 1 | package com.opensource.oneclickaway.android.searchplaces 2 | 3 | import android.app.Activity 4 | import android.app.ActivityOptions 5 | import android.content.Intent 6 | import android.os.Build 7 | import android.os.Bundle 8 | import android.support.v7.app.AppCompatActivity 9 | import android.util.Log 10 | import android.util.Pair 11 | import android.view.View 12 | import android.widget.EditText 13 | import android.widget.TextView 14 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details.PlaceDetails 15 | import com.oneclickaway.opensource.placeautocomplete.ui.SearchPlaceActivity 16 | import com.oneclickaway.opensource.placeautocomplete.utils.SearchPlacesStatusCodes 17 | 18 | /** @author @buren ---> {Sample activity taht implements the feature}*/ 19 | class ExampleLocationSearch : AppCompatActivity() { 20 | 21 | lateinit var searchLocationET: EditText 22 | lateinit var placeDetailsTV: TextView 23 | 24 | var API_KEY = BuildConfig.ApiKey 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | setContentView(R.layout.activity_example_location_search) 29 | 30 | searchLocationET = findViewById(R.id.searchLocationBTN) 31 | placeDetailsTV = findViewById(R.id.resultPlaceDetailsTV) 32 | 33 | val intent = Intent(this, SearchPlaceActivity::class.java) 34 | intent.putExtra( 35 | SearchPlacesStatusCodes.CONFIG, 36 | SearchPlaceActivity.Config.Builder(apiKey = API_KEY) 37 | .setSearchBarTitle("Enter Source Location") 38 | .setMyLocation("12.9716,77.5946") 39 | .setEnclosingRadius("500") 40 | .build() 41 | ) 42 | 43 | searchLocationET.setOnClickListener { 44 | 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 46 | val pair = Pair.create( 47 | (searchLocationET as? View), 48 | SearchPlacesStatusCodes.PLACEHOLDER_TRANSITION 49 | ) 50 | val options = ActivityOptions.makeSceneTransitionAnimation(this, pair).toBundle() 51 | startActivityForResult(intent, 700, options) 52 | 53 | } else { 54 | startActivityForResult(intent, 700) 55 | overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out) 56 | } 57 | 58 | 59 | } 60 | 61 | 62 | } 63 | 64 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 65 | super.onActivityResult(requestCode, resultCode, data) 66 | 67 | if (requestCode == 700 && resultCode == Activity.RESULT_OK) { 68 | 69 | val placeDetails = 70 | data?.getParcelableExtra(SearchPlacesStatusCodes.PLACE_DATA) 71 | 72 | searchLocationET.setText(placeDetails?.name) 73 | 74 | placeDetailsTV.text = placeDetails.toString() 75 | Log.i(javaClass.simpleName, "onActivityResult: ${placeDetails} ") 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 | 116 | 118 |
119 |
-------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/utils/GroupStrategy.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.utils 2 | 3 | import android.util.Log 4 | import com.oneclickaway.opensource.placeautocomplete.data.model.room.SearchSelectedItem 5 | import com.oneclickaway.opensource.placeautocomplete.utils.Commons.getPrettyTime 6 | import java.util.* 7 | import kotlin.collections.ArrayList 8 | 9 | /** 10 | *@author Burhan ud din ---> Class is used to group the list of items in groups 11 | */ 12 | class GroupStrategy { 13 | 14 | /** 15 | *@author Burhan ud din ---> class used to keep time 16 | */ 17 | class TimeContainer(var nowTime: String, var timeInMilliseconds: Long) { 18 | override fun equals(other: Any?): Boolean { 19 | return if (other is TimeContainer) { 20 | this.nowTime == other.nowTime 21 | 22 | } else { 23 | false 24 | } 25 | } 26 | 27 | override fun hashCode(): Int { 28 | Log.d("getHasMap", "hashCode: " + nowTime.hashCode()) 29 | return nowTime.hashCode() 30 | } 31 | 32 | override fun toString(): String { 33 | return "TimeContainer(nowTime='$nowTime', timeInMilliseconds=$timeInMilliseconds)" 34 | } 35 | 36 | 37 | } 38 | 39 | /** 40 | *@author Burhan ud din ---> Liste item in recent search 41 | */ 42 | abstract class ListItem { 43 | 44 | companion object { 45 | const val TYPE_DATE = 101 46 | const val TYPE_GENERAL_ITEM = 102 47 | } 48 | 49 | /** 50 | *@author Burhan ud din ---> Type of item 51 | */ 52 | abstract fun getType(): Int 53 | 54 | } 55 | 56 | /** 57 | *@author Burhan ud din ---> date item in list item 58 | */ 59 | class DateItem : ListItem() { 60 | 61 | lateinit var date: String 62 | 63 | override fun getType() = TYPE_DATE 64 | } 65 | 66 | class GeneralItem : ListItem() { 67 | lateinit var searchSelectedItem: SearchSelectedItem 68 | 69 | override fun getType(): Int = TYPE_GENERAL_ITEM 70 | } 71 | 72 | /** 73 | *@author Burhan ud din ---> Compares and sorts the list into group according to time 74 | */ 75 | class GroupComparator : Comparator { 76 | override fun compare(p0: TimeContainer?, p1: TimeContainer?): Int { 77 | return if (p0!!.timeInMilliseconds < p1!!.timeInMilliseconds) { 78 | 1 79 | } else { 80 | -1 81 | } 82 | 83 | } 84 | 85 | } 86 | 87 | /** 88 | *@author Burhan ud din ---> method used to group the data by time 89 | */ 90 | fun groupDataByTime(myOption: List): ArrayList { 91 | 92 | val consolidatedList = ArrayList() 93 | 94 | val hashedMap = getHasMap(myOption) 95 | 96 | for ((key, value) in hashedMap) { 97 | 98 | val dateItem = DateItem() 99 | dateItem.date = key.nowTime 100 | consolidatedList.add(dateItem) 101 | 102 | for (itemList in value) { 103 | val generalItem = GeneralItem() 104 | generalItem.searchSelectedItem = itemList 105 | consolidatedList.add(generalItem) 106 | } 107 | 108 | 109 | } 110 | 111 | return consolidatedList 112 | 113 | } 114 | 115 | private fun getHasMap(listOfOptions: List): TreeMap> { 116 | 117 | val hashedMap = HashMap>() 118 | 119 | for (searchedItem in listOfOptions) { 120 | 121 | val dateContainer = TimeContainer( 122 | getPrettyTime(searchedItem.searchCurrentMilliseconds), 123 | searchedItem.searchCurrentMilliseconds 124 | ) 125 | if (hashedMap.containsKey(dateContainer)) { 126 | hashedMap[dateContainer]?.add(searchedItem) 127 | } else { 128 | val item = ArrayList() 129 | item.add(searchedItem) 130 | hashedMap[dateContainer] = item 131 | } 132 | 133 | } 134 | 135 | val blankTree = TreeMap>(GroupComparator()) 136 | blankTree.putAll(hashedMap) 137 | 138 | return blankTree 139 | } 140 | 141 | } 142 | 143 | -------------------------------------------------------------------------------- /place_autocomplete/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android-extensions' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 28 8 | 9 | defaultConfig { 10 | minSdkVersion 15 11 | targetSdkVersion 28 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | 19 | androidExtensions { 20 | experimental = true 21 | features = ["parcelize"] 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(dir: 'libs', include: ['*.jar']) 41 | 42 | implementation "org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.3.31" 43 | 44 | implementation 'com.android.support:appcompat-v7:28.0.0' 45 | testImplementation 'junit:junit:4.12' 46 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 47 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 48 | implementation group: 'javax.annotation', name: 'jsr250-api', version: '1.0' 49 | kapt "android.arch.lifecycle:compiler:1.1.1" 50 | testImplementation "android.arch.core:core-testing:1.1.1" 51 | 52 | 53 | /*Please include the libraries mentioned below in your build.gradle */ 54 | 55 | 56 | /*kotlin library*/ 57 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31" 58 | 59 | /*Recycler View and cardview*/ 60 | implementation "com.android.support:recyclerview-v7:28.0.0" 61 | implementation "com.android.support:cardview-v7:28.0.0" 62 | 63 | /*REST Api Libraries*/ 64 | implementation "com.squareup.okhttp3:okhttp:3.14.0" 65 | implementation "com.squareup.retrofit2:retrofit:2.5.0" 66 | implementation "com.squareup.okhttp3:logging-interceptor:3.12.1" 67 | implementation "com.squareup.retrofit2:converter-gson:2.4.0" 68 | 69 | /*RXAndroid and RXJava and RxBinding*/ 70 | implementation "io.reactivex.rxjava2:rxandroid:2.1.1" 71 | implementation "io.reactivex.rxjava2:rxjava:2.2.10" 72 | implementation "com.jakewharton.rxbinding2:rxbinding:2.1.1" 73 | implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0" 74 | 75 | /*life cycle components android jet-pack*/ 76 | implementation "android.arch.lifecycle:extensions:1.1.1" 77 | 78 | /*Glide*/ 79 | implementation 'com.github.bumptech.glide:glide:4.9.0' 80 | annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' 81 | 82 | /*Room implementation*/ 83 | implementation "android.arch.persistence.room:runtime:1.1.1" 84 | annotationProcessor "android.arch.persistence.room:compiler:1.1.1" 85 | kapt "android.arch.persistence.room:compiler:1.1.1" 86 | } 87 | 88 | 89 | repositories { 90 | mavenCentral() 91 | } 92 | 93 | ext { 94 | bintrayRepo = 'PlaceAutocomplete' 95 | bintrayName = 'com.opensource.oneclickaway.android.searchplaces' 96 | 97 | publishedGroupId = 'com.opensource.oneclickaway.android.searchplaces' 98 | libraryName = 'PlaceAutocomplete' 99 | artifact = 'place_autocomplete' //This artifact name should be the same with library module name 100 | 101 | libraryDescription = 'A powerful library that provides you with places search api' 102 | 103 | siteUrl = 'https://github.com/drabu/PlaceAutocomplete' 104 | gitUrl = 'https://github.com/drabu/PlaceAutocomplete' 105 | 106 | libraryVersion = '1.3.1.1' 107 | 108 | developerId = 'drabu' 109 | developerName = 'Burhan ud din Drabu' 110 | developerEmail = 'burhan.drabu@gmail.com' 111 | organization = 'drabu'// if you push to organization's repository. 112 | licenseName = 'MIT License' //Example for license 113 | licenseUrl = 'https://github.com/Drabu/PlaceAutocomplete/blob/master/LICENSE' 114 | allLicenses = ["MIT License"] 115 | 116 | } 117 | 118 | 119 | //These two remote files contain Bintray configuration as described above. 120 | apply from: 'https://raw.githubusercontent.com/quangctkm9207/template-files/master/android/gradle/install.gradle' 121 | apply from: 'https://raw.githubusercontent.com/quangctkm9207/template-files/master/android/gradle/bintray.gradle' -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/adapter/RecentSearchesAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.adapter 2 | 3 | import android.support.v7.widget.RecyclerView 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import com.oneclickaway.opensource.placeautocomplete.R 9 | import com.oneclickaway.opensource.placeautocomplete.interfaces.SearchPlaces 10 | import com.oneclickaway.opensource.placeautocomplete.utils.GroupStrategy 11 | 12 | /** 13 | *@author Burhan ud din ---> Search adapter 14 | */ 15 | class RecentSearchesAdapter( 16 | val listSearchSelectedItem: List, 17 | var recentOnItemItemSelectedListener: SearchPlaces.RecentItemSelectedListener 18 | ) : 19 | RecyclerView.Adapter() { 20 | override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { 21 | 22 | when (viewHolder.itemViewType) { 23 | 24 | GroupStrategy.ListItem.TYPE_DATE -> { 25 | setDateView( 26 | viewHolder as DateViewHolder, 27 | listSearchSelectedItem[position] as GroupStrategy.DateItem 28 | ) 29 | } 30 | 31 | GroupStrategy.ListItem.TYPE_GENERAL_ITEM -> { 32 | setSearchedItem( 33 | viewHolder as GeneralItemViewHolder, 34 | listSearchSelectedItem[position] as GroupStrategy.GeneralItem 35 | ) 36 | } 37 | 38 | } 39 | 40 | } 41 | 42 | private fun setSearchedItem( 43 | generalItemViewHolder: GeneralItemViewHolder, 44 | generalItem: GroupStrategy.GeneralItem 45 | ) { 46 | generalItemViewHolder.recentPlaceTitleTV.text = generalItem.searchSelectedItem.mainText 47 | generalItemViewHolder.recentPlaceFormattedAddressTV.text = 48 | generalItem.searchSelectedItem.secondaryText 49 | } 50 | 51 | private fun setDateView(dateViewHolder: DateViewHolder, dateItem: GroupStrategy.DateItem) { 52 | dateViewHolder.groupTitleTV.text = dateItem.date.toUpperCase() 53 | } 54 | 55 | override fun onCreateViewHolder( 56 | viewGroup: ViewGroup, 57 | itemViewType: Int 58 | ): RecyclerView.ViewHolder { 59 | 60 | if (itemViewType == GroupStrategy.ListItem.TYPE_GENERAL_ITEM) { 61 | 62 | return GeneralItemViewHolder( 63 | LayoutInflater.from(viewGroup.context).inflate( 64 | R.layout.recent_search_result_row, 65 | viewGroup, 66 | false 67 | ) 68 | ) 69 | 70 | } else { 71 | 72 | return DateViewHolder( 73 | LayoutInflater.from(viewGroup.context).inflate( 74 | R.layout.date_item_row, 75 | viewGroup, 76 | false 77 | ) 78 | ) 79 | } 80 | 81 | } 82 | 83 | 84 | override fun getItemViewType(position: Int): Int { 85 | 86 | when (listSearchSelectedItem[position]) { 87 | 88 | is GroupStrategy.DateItem -> { 89 | return GroupStrategy.ListItem.TYPE_DATE 90 | } 91 | 92 | is GroupStrategy.GeneralItem -> { 93 | return GroupStrategy.ListItem.TYPE_GENERAL_ITEM 94 | } 95 | 96 | } 97 | 98 | 99 | return -1 100 | } 101 | 102 | override fun getItemCount(): Int { 103 | return listSearchSelectedItem.size 104 | } 105 | 106 | /** 107 | *@author Burhan ud din ---> Data view holder 108 | */ 109 | inner class DateViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 110 | 111 | lateinit var groupTitleTV: TextView 112 | 113 | init { 114 | inflateView() 115 | } 116 | 117 | private fun inflateView() { 118 | groupTitleTV = itemView.findViewById(R.id.groupTitleTV) 119 | } 120 | } 121 | 122 | 123 | /** 124 | *@author Burhan ud din ---> document holder 125 | */ 126 | inner class GeneralItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 127 | 128 | 129 | lateinit var recentPlaceTitleTV: TextView 130 | lateinit var recentPlaceFormattedAddressTV: TextView 131 | 132 | init { 133 | inflateView() 134 | 135 | setOnClickLister(itemView) 136 | } 137 | 138 | private fun setOnClickLister(itemView: View) { 139 | itemView.setOnClickListener { 140 | recentOnItemItemSelectedListener.onRecantsItemSelected(listSearchSelectedItem[adapterPosition]) 141 | } 142 | } 143 | 144 | private fun inflateView() { 145 | recentPlaceFormattedAddressTV = 146 | itemView.findViewById(R.id.recentPlaceFormattedAddressTV) 147 | recentPlaceTitleTV = itemView.findViewById(R.id.recentPlaceTitleTV) 148 | } 149 | } 150 | 151 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PlaceAutocomplete 2 | ======== 3 | Google Places Api Implementation 4 | 5 | [![Download](https://api.bintray.com/packages/drabu/PlaceAutocomplete/com.opensource.oneclickaway.android.searchplaces/images/download.svg) ](https://bintray.com/drabu/PlaceAutocomplete/com.opensource.oneclickaway.android.searchplaces/_latestVersion) 6 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/34c0864ec96f4ce8a094a60d040e7ff7)](https://www.codacy.com/app/Drabu/PlaceAutocomplete?utm_source=github.com&utm_medium=referral&utm_content=Drabu/PlaceAutocomplete&utm_campaign=Badge_Grade) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 8 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-PlaceAutocomplete-blue.svg?style=flat)](https://android-arsenal.com/details/1/7655) 9 | 10 | After Google came up with the update that Play Services version of the Places SDK for Android (in Google Play Services 16.0.0) is deprecated as of January 29, 2019, and will be turned off on July 29, 2019. 11 | which includes PlaceAutocompleteFragment also to be removed from the library.After getting this update from google we had to immediately migrate to an alternative where we ended up creating this library, PlaceAutocompleteFragment provides you with an elegant user interface to choose a location and works the exact same as PlaceAutocompleteFragment . 12 | 13 | ### Features 14 | Custom title message for the view. 15 | 16 | Sorted places based on your location provided you pass your location. 17 | 18 | Radius of enclosure for search 19 | 20 | Support for activity animation for a smooth transition 21 | 22 | Support for landscape orientation. 23 | 24 | This Kotlin Library is build with MVVM Archetecture and runs on top of RxJava 2. The library uses various Rx operators like debounce, throttlefirst operators to reduce network calls and optimze location query. 25 | 26 | ### What's New 27 | 28 | #1 Two different loaders attached, one for getting the list of predicted items and one for getting place details.
29 | #2 Erase query with a close icon added to clear the text in the field.
30 | #3 Keeps a record of Recent searches (Be Sure to add the dependency).
31 | #4 Maintains recent searches in pretty time groups like Today, Yesterday, Earlier this week and previous searches.
32 | #5 Internet connectivity and error handling also added wherever needed 33 | 34 | ### Dependencies 35 | 36 | Since this library is build with MVVM Architecture and uses Kotlin, RxAndroid, RxJava 2, Rx Binding,recyclerview, cardview, retrofit and various android architecture components, so you can find the dependencies [here](https://github.com/Drabu/PlaceAutocomplete/blob/master/place_autocomplete/build.gradle). and add them to your build.gradle file. 37 | 38 | ### How it works 39 | 40 |

41 | Demonstartion image. 42 |

43 | 44 | Configuration 45 | ------------- 46 | 47 | Add the dependency: 48 | 49 | dependencies { 50 | //copy the version from download badge above 51 | implementation 'com.opensource.oneclickaway.android.searchplaces:place_autocomplete:x.x.x' 52 | } 53 | 54 | Mandatory and optional parameters : 55 | 56 | val intent = Intent(this, SearchPlaceActivity::class.java) 57 | intent.putExtra( 58 | SearchPlacesStatusCodes.CONFIG, 59 | SearchPlaceActivity.Config.Builder(apiKey = "PASS YOUR API KEY HERE") 60 | .setSearchBarTitle("Enter Source Location") 61 | .setMyLocation("12.9716,77.5946") 62 | .setEnclosingRadius("500") 63 | .build() 64 | ) 65 | 66 | For build version greater LOLLIPOP, you can use Activity Transition like this: 67 | 68 | val pair = Pair.create(searchLocationET as View, SearchPlacesStatusCodes.PLACEHOLDER_TRANSITION) 69 | val options = ActivityOptions.makeSceneTransitionAnimation(this, pair).toBundle() 70 | startActivityForResult(intent, 700, options) 71 | 72 | #Example Kotlin Class: 73 | 74 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details.PlaceDetails 75 | import com.oneclickaway.opensource.placeautocomplete.components.SearchPlacesStatusCodes 76 | import com.oneclickaway.opensource.placeautocomplete.ui.SearchPlaceActivity 77 | 78 | class ExampleLocationSearch : AppCompatActivity() { 79 | 80 | lateinit var searchLocationET: EditText 81 | lateinit var placeDetailsTV: TextView 82 | var REQUEST_CODE = 700 83 | 84 | var API_KEY = BuildConfig.ApiKey 85 | 86 | override fun onCreate(savedInstanceState: Bundle?) { 87 | super.onCreate(savedInstanceState) 88 | setContentView(R.layout.activity_example_location_search) 89 | 90 | searchLocationET = findViewById(R.id.searchLocationBTN) 91 | placeDetailsTV = findViewById(R.id.resultPlaceDetailsTV) 92 | 93 | val intent = Intent(this, SearchPlaceActivity::class.java) 94 | intent.putExtra( 95 | SearchPlacesStatusCodes.CONFIG, 96 | SearchPlaceActivity.Config.Builder(apiKey = API_KEY) 97 | .setSearchBarTitle("Enter Source Location") 98 | .setMyLocation("12.9716,77.5946") 99 | .setEnclosingRadius("500") 100 | .build() 101 | ) 102 | 103 | searchLocationET.setOnClickListener { 104 | 105 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 106 | val pair = Pair.create(searchLocationET as View, SearchPlacesStatusCodes.PLACEHOLDER_TRANSITION) 107 | val options = ActivityOptions.makeSceneTransitionAnimation(this, pair).toBundle() 108 | startActivityForResult(intent, REQUEST_CODE, options) 109 | 110 | } else { 111 | startActivityForResult(intent, REQUEST_CODE) 112 | overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out) 113 | } 114 | 115 | 116 | } 117 | 118 | 119 | } 120 | 121 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 122 | super.onActivityResult(requestCode, resultCode, data) 123 | 124 | if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) { 125 | 126 | val placeDetails = data?.getParcelableExtra(SearchPlacesStatusCodes.PLACE_DATA) 127 | 128 | searchLocationET.setText(placeDetails?.name) 129 | 130 | placeDetailsTV.text = placeDetails.toString() 131 | Log.i(javaClass.simpleName, "onActivityResult: ${placeDetails} ") 132 | } 133 | } 134 | } 135 | 136 | Usage 137 | ----- 138 | -Minimum sdk 15.
139 | -Returns place information in onActivityResult 140 | 141 | License 142 | ----- 143 | MIT License 144 | 145 | Copyright (c) 2019 Burhan ud din 146 | 147 | Permission is hereby granted, free of charge, to any person obtaining a copy 148 | of this software and associated documentation files (the "Software"), to deal 149 | in the Software without restriction, including without limitation the rights 150 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 151 | copies of the Software, and to permit persons to whom the Software is 152 | furnished to do so, subject to the following conditions: 153 | 154 | The above copyright notice and this permission notice shall be included in all 155 | copies or substantial portions of the Software. 156 | 157 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 159 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 160 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 161 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 162 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 163 | SOFTWARE. 164 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/res/layout/activity_search_place.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 21 | 22 | 29 | 30 | 39 | 40 | 46 | 47 | 48 | 49 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | 85 | 86 | 91 | 92 | 100 | 101 | 110 | 111 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 134 | 135 | 140 | 141 | 152 | 153 | 154 | 156 | 157 | 158 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 189 | 190 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/data/repositories/SearchPlacesRepo.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.data.repositories 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import android.arch.lifecycle.LiveData 6 | import android.arch.lifecycle.MutableLiveData 7 | import android.content.Context 8 | import android.util.Log 9 | import com.oneclickaway.opensource.placeautocomplete.data.api.base.RESTAPIManager 10 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.place_details.PlaceDetails 11 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response.PredictionsItem 12 | import com.oneclickaway.opensource.placeautocomplete.data.model.room.SearchSelectedItem 13 | import com.oneclickaway.opensource.placeautocomplete.data.room.RecentSearchesDB 14 | import com.oneclickaway.opensource.placeautocomplete.utils.Commons.isNetworkConnected 15 | import com.oneclickaway.opensource.placeautocomplete.utils.LoadingManager 16 | import com.oneclickaway.opensource.placeautocomplete.utils.SearchPlacesStatusCodes 17 | import io.reactivex.Observable 18 | import io.reactivex.observers.DisposableObserver 19 | import io.reactivex.schedulers.Schedulers 20 | 21 | /** @author @buren ---> {REST Service repository}*/ 22 | @SuppressLint("CheckResult") 23 | class SearchPlacesRepo(var application: Application) { 24 | 25 | 26 | /*Data from lists here*/ 27 | 28 | private val listOfSearchResults: MutableLiveData> = MutableLiveData() 29 | /** @author @buren ---> {micro-service used to get live data stream of search results}*/ 30 | fun getLiveListOfSearchResultsStream(): LiveData> = listOfSearchResults 31 | 32 | private val placeDetails: MutableLiveData = MutableLiveData() 33 | /** @author @buren ---> {micro-service used to get live data stream of place details}*/ 34 | fun getPlaceDetailsLiveDataStream(): LiveData = placeDetails 35 | 36 | private val recentSearches: MutableLiveData> = MutableLiveData() 37 | /** @author @buren ---> {micro-service used to get live data stream of recent search data}*/ 38 | fun getRecentSearches(): LiveData> = recentSearches 39 | 40 | /*Data lists ends here*/ 41 | 42 | 43 | /*Loading managers are mentioned here*/ 44 | 45 | private val loadingPredictionManager = MutableLiveData() 46 | /** 47 | *@author Burhan ud din ---> Loading prection manager 48 | */ 49 | fun getLoadingPredictionManager(): LiveData = loadingPredictionManager 50 | 51 | private val loadingPlaceManager = MutableLiveData() 52 | /** 53 | *@author Burhan ud din ---> Loading place manager 54 | */ 55 | fun getLoadingPlaceManager(): LiveData = loadingPlaceManager 56 | 57 | private val recentSearchesManager = MutableLiveData() 58 | /** 59 | *@author Burhan ud din ---> gets the search manager to alter visibility 60 | */ 61 | fun getRecentSearchesManager(): LiveData = recentSearchesManager 62 | 63 | /*loading managers ends here*/ 64 | 65 | 66 | /*request from view for data */ 67 | 68 | /** @author @buren ---> {this micro-service gets autocomplete results}*/ 69 | fun requestListOfSearchResults( 70 | placeHint: String, 71 | apiKey: String, 72 | location: String, 73 | radius: String 74 | ) { 75 | loadingPredictionManager.postValue(LoadingManager.STATE_REFRESHING) 76 | 77 | if (placeHint.isBlank()) { 78 | /*search box contains location query checking internet connection*/ 79 | loadingPredictionManager.postValue(LoadingManager.STATE_IDLE) 80 | return 81 | } 82 | 83 | 84 | if (!isNetworkConnected(application)) { 85 | loadingPredictionManager.postValue(LoadingManager.STATE_NO_INTERNET) 86 | return 87 | } 88 | 89 | RESTAPIManager.getInstance().getPlaceResults( 90 | placeHint = placeHint, 91 | apiKey = apiKey, 92 | location = location, 93 | radius = radius 94 | ) 95 | .filter { 96 | if (it.predictions != null && it.predictions.isEmpty()) { 97 | loadingPredictionManager.postValue(LoadingManager.STATE_NO_RESULT) 98 | return@filter false 99 | } else { 100 | return@filter true 101 | } 102 | } 103 | .subscribeOn(Schedulers.io()) 104 | .observeOn(Schedulers.io()) 105 | .map { 106 | it.predictions 107 | } 108 | .subscribe(object : DisposableObserver>() { 109 | override fun onComplete() { 110 | loadingPredictionManager.postValue(LoadingManager.STATE_COMPLETED) 111 | } 112 | 113 | override fun onNext(t: List) { 114 | listOfSearchResults.postValue(t) 115 | } 116 | 117 | override fun onError(e: Throwable) { 118 | loadingPredictionManager.postValue(LoadingManager.STATE_ERROR) 119 | } 120 | 121 | }) 122 | 123 | 124 | } 125 | 126 | /** @author @buren ---> {micro-service used to get place details}*/ 127 | fun requestPlaceDetails(placeId: String, apiKey: String) { 128 | loadingPlaceManager.postValue(LoadingManager.STATE_REFRESHING) 129 | 130 | if (!isNetworkConnected(application)) { 131 | loadingPlaceManager.postValue(LoadingManager.STATE_NO_INTERNET) 132 | return 133 | } 134 | 135 | RESTAPIManager.getInstance().getPlaceDetailsFromPlaceId(placeId = placeId, apiKey = apiKey) 136 | .observeOn(Schedulers.io()) 137 | .subscribeOn(Schedulers.io()) 138 | .filter { 139 | it.status.equals(SearchPlacesStatusCodes.GOOGLE_SEARCH_RESULT_OK) 140 | }.map { 141 | it.result 142 | }.subscribe(object : DisposableObserver() { 143 | 144 | override fun onComplete() { 145 | loadingPlaceManager.postValue(LoadingManager.STATE_COMPLETED) 146 | } 147 | 148 | override fun onNext(it: PlaceDetails) { 149 | placeDetails.postValue(it) 150 | addSearchedItemToRecent(it) 151 | 152 | 153 | } 154 | 155 | override fun onError(e: Throwable) { 156 | loadingPlaceManager.postValue(LoadingManager.STATE_ERROR) 157 | } 158 | 159 | }) 160 | 161 | } 162 | 163 | /** @author @buren ---> {micro-service used to get recent searches}*/ 164 | fun requestListOfRecentSearches(mContext: Context) { 165 | recentSearchesManager.postValue(LoadingManager.STATE_REFRESHING) 166 | 167 | if (!isNetworkConnected(application)) { 168 | recentSearchesManager.postValue(LoadingManager.STATE_NO_INTERNET) 169 | return 170 | } 171 | 172 | Observable.fromCallable { 173 | RecentSearchesDB.getInstance(mContext)?.repDao()?.getRecentSearches() 174 | } 175 | .subscribeOn(Schedulers.io()) 176 | .observeOn(Schedulers.io()) 177 | .subscribe { 178 | if (it != null && it.isEmpty()) { 179 | recentSearchesManager.postValue(LoadingManager.STATE_NO_RESULT) 180 | } else { 181 | recentSearchesManager.postValue(LoadingManager.STATE_COMPLETED) 182 | Log.d("arrayItems", "" + it.toString()) 183 | recentSearches.postValue(it) 184 | 185 | 186 | } 187 | } 188 | 189 | } 190 | 191 | /** 192 | *@author Burhan ud din ---> Adding item to recent dearch 193 | */ 194 | fun addSearchedItemToRecent(it: PlaceDetails) { 195 | Observable.fromCallable { 196 | initDb()?.repDao()?.addSearchItem( 197 | SearchSelectedItem( 198 | placeId = it.placeId.toString(), 199 | mainText = it.name.toString(), 200 | secondaryText = it.formattedAddress.toString(), 201 | searchCurrentMilliseconds = System.currentTimeMillis() 202 | ) 203 | ) 204 | } 205 | .subscribeOn(Schedulers.computation()) 206 | .observeOn(Schedulers.io()) 207 | .subscribe(object : DisposableObserver() { 208 | override fun onNext(t: Unit) { 209 | Log.e(javaClass.simpleName, "addSearchedItemToRecent: onNext") 210 | } 211 | 212 | override fun onComplete() { 213 | Log.e(javaClass.simpleName, "addSearchedItemToRecent: Completed") 214 | } 215 | 216 | 217 | override fun onError(e: Throwable) { 218 | Log.e(javaClass.simpleName, "addSearchedItemToRecent: Error") 219 | } 220 | 221 | }) 222 | 223 | 224 | } 225 | 226 | 227 | private fun initDb(): RecentSearchesDB? { 228 | return RecentSearchesDB.getInstance(application) 229 | 230 | } 231 | } 232 | 233 | /*request from view ends here*/ 234 | 235 | 236 | -------------------------------------------------------------------------------- /place_autocomplete/src/main/java/com/oneclickaway/opensource/placeautocomplete/ui/SearchPlaceActivity.kt: -------------------------------------------------------------------------------- 1 | package com.oneclickaway.opensource.placeautocomplete.ui 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Activity 5 | import android.app.Dialog 6 | import android.arch.lifecycle.MutableLiveData 7 | import android.arch.lifecycle.Observer 8 | import android.arch.lifecycle.ViewModelProviders 9 | import android.content.Intent 10 | import android.os.Build 11 | import android.os.Bundle 12 | import android.os.Parcelable 13 | import android.support.v7.app.AppCompatActivity 14 | import android.support.v7.widget.LinearLayoutManager 15 | import android.support.v7.widget.RecyclerView 16 | import android.util.Log 17 | import android.view.View 18 | import android.view.View.GONE 19 | import android.view.View.VISIBLE 20 | import android.widget.* 21 | import com.bumptech.glide.Glide 22 | import com.jakewharton.rxbinding2.widget.RxTextView 23 | import com.oneclickaway.opensource.placeautocomplete.R 24 | import com.oneclickaway.opensource.placeautocomplete.data.adapter.RecentSearchesAdapter 25 | import com.oneclickaway.opensource.placeautocomplete.data.adapter.SearchResultAdapter 26 | import com.oneclickaway.opensource.placeautocomplete.data.api.bean.places_response.PredictionsItem 27 | import com.oneclickaway.opensource.placeautocomplete.data.model.view.SearchPlacesViewModel 28 | import com.oneclickaway.opensource.placeautocomplete.data.room.RecentSearchesDB 29 | import com.oneclickaway.opensource.placeautocomplete.interfaces.SearchPlaces 30 | import com.oneclickaway.opensource.placeautocomplete.utils.GroupStrategy 31 | import com.oneclickaway.opensource.placeautocomplete.utils.LoadingManager 32 | import com.oneclickaway.opensource.placeautocomplete.utils.LoadingManager.* 33 | import com.oneclickaway.opensource.placeautocomplete.utils.SearchPlacesStatusCodes 34 | import io.reactivex.android.schedulers.AndroidSchedulers 35 | import io.reactivex.observers.DisposableObserver 36 | import io.reactivex.schedulers.Schedulers 37 | import kotlinx.android.parcel.Parcelize 38 | import java.util.concurrent.TimeUnit 39 | 40 | /** @author @buren ---> {This activity will take care of picking the place and returning back the response}*/ 41 | @SuppressLint("CheckResult") 42 | class SearchPlaceActivity : AppCompatActivity(), SearchPlaces.PlaceItemSelectedListener, 43 | SearchPlaces.RecentItemSelectedListener, View.OnClickListener { 44 | 45 | private lateinit var viewModel: SearchPlacesViewModel 46 | private lateinit var searchListAdapter: SearchResultAdapter 47 | lateinit var gettingPlaceDataDialog: Dialog 48 | 49 | private var apiKey: String? = null 50 | private var location: String? = null 51 | private var enclosingRadius: String? = null 52 | 53 | private var liveQueryInEditText: MutableLiveData = MutableLiveData() 54 | /*view*/ 55 | 56 | lateinit var searchTitleTV: TextView 57 | lateinit var searchProgressBar: ProgressBar 58 | lateinit var secondaryInformationLL: LinearLayout 59 | lateinit var placeNamET: EditText 60 | lateinit var searchResultsRV: RecyclerView 61 | 62 | lateinit var secondaryInfoSubTitleTV: TextView 63 | lateinit var secondaryInfoTitleTV: TextView 64 | lateinit var backImageBtn: ImageView 65 | 66 | lateinit var eraseCurrentEntryIV: ImageView 67 | 68 | var recentSearchesDB: RecentSearchesDB? = null 69 | var recentSearchesLL: LinearLayout? = null 70 | 71 | lateinit var recentSearchesRV: RecyclerView 72 | 73 | override fun onCreate(savedInstanceState: Bundle?) { 74 | super.onCreate(savedInstanceState) 75 | setContentView(R.layout.activity_search_place) 76 | 77 | setViewModel() 78 | 79 | initDb() 80 | 81 | inflateViews() 82 | 83 | initializeDependency() 84 | 85 | initDialogForGettingPlaceDetails() 86 | 87 | setOnClickListeners() 88 | 89 | setRecyclerView() 90 | 91 | setOnQueryChangeListener() 92 | 93 | attachLiveObservers() 94 | 95 | 96 | } 97 | 98 | private fun initDb() { 99 | recentSearchesDB = RecentSearchesDB.getInstance(this) 100 | getViewModel().requestListOfRecentSearches() 101 | } 102 | 103 | private fun inflateViews() { 104 | 105 | searchTitleTV = findViewById(R.id.searchTitleTV) 106 | searchProgressBar = findViewById(R.id.searchProgressBar) 107 | secondaryInformationLL = findViewById(R.id.secondaryInformationLL) 108 | placeNamET = findViewById(R.id.placeNamET) 109 | searchResultsRV = findViewById(R.id.searchResultsRV) 110 | secondaryInfoTitleTV = findViewById(R.id.secondaryInfoTitleTV) 111 | secondaryInfoSubTitleTV = findViewById(R.id.secondaryInfoSubTitleTV) 112 | backImageBtn = findViewById(R.id.backImageBtn) 113 | recentSearchesRV = findViewById(R.id.recentSearchesRV) 114 | recentSearchesLL = findViewById(R.id.recentSearchesLL) 115 | eraseCurrentEntryIV = findViewById(R.id.eraseCurrentEntryIV) 116 | 117 | } 118 | 119 | private fun setOnClickListeners() { 120 | backImageBtn.setOnClickListener(this) 121 | eraseCurrentEntryIV.setOnClickListener(this) 122 | } 123 | 124 | 125 | private fun setSecondaryStateInformation(pageStatus: LoadingManager) { 126 | 127 | searchResultsRV.visibility = GONE 128 | searchProgressBar.visibility = GONE 129 | secondaryInformationLL.visibility = VISIBLE 130 | 131 | 132 | when (pageStatus) { 133 | 134 | STATE_NO_INTERNET -> { 135 | secondaryInfoTitleTV.text = "No internet" 136 | secondaryInfoSubTitleTV.text = "Please connect to internet and try again" 137 | } 138 | 139 | STATE_ERROR -> { 140 | secondaryInfoTitleTV.text = "Oops!" 141 | secondaryInfoSubTitleTV.text = "We're having some trouble connecting to our servers" 142 | } 143 | 144 | STATE_NO_RESULT -> { 145 | secondaryInfoTitleTV.text = getString(R.string.location_not_found) 146 | secondaryInfoSubTitleTV.text = getString(R.string.please_check_spell_errors) 147 | } 148 | 149 | else -> { 150 | print("no state detected") 151 | } 152 | } 153 | 154 | } 155 | 156 | private fun initializeDependency() { 157 | 158 | 159 | if (intent.hasExtra(SearchPlacesStatusCodes.CONFIG)) { 160 | 161 | val configuration = intent.extras?.getParcelable(SearchPlacesStatusCodes.CONFIG) 162 | apiKey = configuration?.apiKey 163 | location = configuration?.location 164 | enclosingRadius = configuration?.enclosingRadius 165 | searchTitleTV.text = configuration?.searchBarTitle 166 | 167 | } else { 168 | /*finish*/ 169 | Toast.makeText(this, "Please mention the api key in put-extra", Toast.LENGTH_LONG) 170 | .show() 171 | finish() 172 | } 173 | 174 | } 175 | 176 | private fun attachLiveObservers() { 177 | 178 | getViewModel().getLiveListOfSearchResultsStream().observe(this, Observer { 179 | //refresh the adapter here 180 | searchListAdapter.setSearchCandidates(it) 181 | 182 | }) 183 | 184 | getViewModel().getPlaceDetailsLiveDataStream().observe(this, Observer { 185 | 186 | if (it != null) { 187 | val resultData = Intent() 188 | resultData.putExtra(SearchPlacesStatusCodes.PLACE_DATA, it) 189 | setResult(Activity.RESULT_OK, resultData) 190 | } 191 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 192 | finishAfterTransition() 193 | } else { 194 | finish() 195 | overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out) 196 | } 197 | 198 | }) 199 | 200 | getViewModel().getLoadingPredictionManager().observe(this, Observer { 201 | 202 | when (it) { 203 | 204 | STATE_REFRESHING -> { 205 | 206 | recentSearchesLL!!.visibility = GONE 207 | searchProgressBar.visibility = View.VISIBLE 208 | /*user is searching a random world*/ 209 | if (secondaryInformationLL.visibility == VISIBLE) { 210 | /*STATE_REFRESHING */ 211 | } else { 212 | searchResultsRV.visibility = View.VISIBLE 213 | } 214 | } 215 | 216 | STATE_COMPLETED -> { 217 | recentSearchesLL!!.visibility = GONE 218 | searchResultsRV.visibility = View.VISIBLE 219 | searchProgressBar.visibility = View.GONE 220 | secondaryInformationLL.visibility = View.GONE 221 | } 222 | 223 | 224 | STATE_NO_INTERNET -> { 225 | recentSearchesLL!!.visibility = GONE 226 | setSecondaryStateInformation(STATE_NO_INTERNET) 227 | } 228 | 229 | 230 | STATE_ERROR -> { 231 | recentSearchesLL!!.visibility = GONE 232 | Log.d("VISIBLITY", "STATE_ERROR") 233 | setSecondaryStateInformation(STATE_ERROR) 234 | } 235 | 236 | STATE_NO_RESULT -> { 237 | recentSearchesLL!!.visibility = GONE 238 | setSecondaryStateInformation(STATE_NO_RESULT) 239 | 240 | } 241 | 242 | STATE_IDLE -> { 243 | eraseCurrentEntryIV.visibility = GONE 244 | searchResultsRV.visibility = GONE 245 | searchProgressBar.visibility = GONE 246 | secondaryInformationLL.visibility = GONE 247 | recentSearchesLL!!.visibility = VISIBLE 248 | } 249 | 250 | 251 | else -> print("No loading state detected") 252 | } 253 | 254 | }) 255 | 256 | getViewModel().getLoadingPlaceManager().observe(this, Observer { 257 | 258 | when (it) { 259 | 260 | STATE_REFRESHING -> { 261 | gettingPlaceDataDialog.show() 262 | } 263 | 264 | STATE_COMPLETED -> { 265 | gettingPlaceDataDialog.cancel() 266 | } 267 | 268 | 269 | STATE_NO_INTERNET -> { 270 | gettingPlaceDataDialog.cancel() 271 | Toast.makeText(this, getString(R.string.no_internet), Toast.LENGTH_LONG).show() 272 | } 273 | 274 | 275 | STATE_ERROR -> { 276 | gettingPlaceDataDialog.cancel() 277 | Toast.makeText( 278 | this, 279 | getString(R.string.trouble_getting_data), 280 | Toast.LENGTH_LONG 281 | ).show() 282 | } 283 | 284 | 285 | else -> print("No loading state detected") 286 | } 287 | 288 | }) 289 | 290 | getViewModel().getRecentSearchesManager().observe(this, Observer { 291 | 292 | when (it) { 293 | 294 | STATE_COMPLETED -> { 295 | } 296 | 297 | STATE_REFRESHING -> { 298 | } 299 | 300 | STATE_ERROR -> { 301 | Toast.makeText(this, "State Error", Toast.LENGTH_LONG).show() 302 | } 303 | 304 | STATE_NO_INTERNET -> { 305 | 306 | } 307 | 308 | STATE_NO_RESULT -> { 309 | } 310 | 311 | 312 | else -> { 313 | 314 | 315 | } 316 | } 317 | }) 318 | 319 | getViewModel().getRecentSearchesData().observe(this, Observer { 320 | if (it != null) { 321 | recentSearchesRV.layoutManager = LinearLayoutManager(this) 322 | recentSearchesRV.adapter = 323 | RecentSearchesAdapter(GroupStrategy().groupDataByTime(it), this) 324 | } 325 | }) 326 | 327 | attachEraserObserver() 328 | 329 | } 330 | 331 | private fun attachEraserObserver() { 332 | 333 | 334 | liveQueryInEditText.observe(this, Observer { 335 | 336 | if (it != null && it.isNotEmpty()) { 337 | eraseCurrentEntryIV.visibility = VISIBLE 338 | } else { 339 | eraseCurrentEntryIV.visibility = GONE 340 | 341 | } 342 | }) 343 | 344 | } 345 | 346 | 347 | 348 | 349 | private fun setViewModel() { 350 | viewModel = ViewModelProviders.of(this).get(SearchPlacesViewModel::class.java) 351 | } 352 | 353 | private fun setRecyclerView() { 354 | 355 | searchResultsRV.layoutManager = LinearLayoutManager(this) 356 | searchListAdapter = 357 | SearchResultAdapter(placeItemSelectedListener = this) 358 | searchResultsRV.adapter = searchListAdapter 359 | 360 | } 361 | 362 | private fun setOnQueryChangeListener() { 363 | 364 | RxTextView.textChanges(placeNamET) 365 | .debounce(500, TimeUnit.MILLISECONDS) 366 | .observeOn(AndroidSchedulers.mainThread()) 367 | .subscribeOn(Schedulers.io()) 368 | .subscribeWith(object : DisposableObserver() { 369 | override fun onComplete() { 370 | print("Completed") 371 | } 372 | 373 | override fun onNext(t: CharSequence) { 374 | 375 | liveQueryInEditText.postValue(t.toString()) 376 | 377 | getViewModel().requestListOfSearchResults( 378 | placeHint = t.toString(), 379 | apiKey = apiKey!!, 380 | location = location ?: "", 381 | radius = enclosingRadius ?: "500" 382 | ) 383 | 384 | } 385 | 386 | override fun onError(e: Throwable) { 387 | e.printStackTrace() 388 | } 389 | 390 | }) 391 | 392 | } 393 | 394 | 395 | override fun onBackPressed() { 396 | super.onBackPressed() 397 | overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out) 398 | } 399 | 400 | override fun onClick(p0: View?) { 401 | 402 | when (p0?.id) { 403 | 404 | R.id.backImageBtn -> { 405 | onBackPressed() 406 | } 407 | 408 | R.id.eraseCurrentEntryIV -> { 409 | placeNamET.text.clear() 410 | } 411 | 412 | } 413 | 414 | } 415 | 416 | /** @author @buren ---> {Configuration class for Search places} 417 | * 418 | * @param apiKey This is a mandatory field which is used to query google places api 419 | * @param enclosingRadius (radius in meters from current location) This is an optional field which is required to search a location within a specific radius 420 | * @param location You can pass your location which sorts the list as per your nearest location 421 | * @param searchBarTitle This can be used to set title for search activity 422 | * 423 | * */ 424 | @Parcelize 425 | class Config( 426 | var apiKey: String, 427 | var location: String = "", 428 | var enclosingRadius: String = "", 429 | var searchBarTitle: String = "Enter Location" 430 | ) : Parcelable { 431 | 432 | @Parcelize 433 | class Builder(var apiKey: String) : Parcelable { 434 | 435 | var location: String = "" 436 | var enclosingRadius: String = "" 437 | var searchBarTitle: String = "Enter Location" 438 | 439 | /** 440 | *@author Burhan ud din ---> Set location in lat, lng which sorts location resulsts based on your lcoation 441 | */ 442 | fun setMyLocation(location: String): Builder { 443 | this.location = location 444 | return this 445 | } 446 | 447 | /** 448 | *@author Burhan ud din ---> enclosing radius 449 | */ 450 | fun setEnclosingRadius(enclosingRadius: String): Builder { 451 | this.enclosingRadius = enclosingRadius 452 | return this 453 | } 454 | 455 | /** 456 | *@author Burhan ud din ---> Title of the bar 457 | */ 458 | fun setSearchBarTitle(searchBarTitle: String): Builder { 459 | this.searchBarTitle = searchBarTitle 460 | return this 461 | } 462 | 463 | /** 464 | *@author Burhan ud din ---> Build the object 465 | */ 466 | fun build(): Config { 467 | return Config( 468 | apiKey = apiKey, 469 | enclosingRadius = enclosingRadius, 470 | location = location, 471 | searchBarTitle = searchBarTitle 472 | ) 473 | } 474 | 475 | 476 | } 477 | 478 | 479 | } 480 | 481 | private fun initDialogForGettingPlaceDetails() { 482 | // custom dialog 483 | gettingPlaceDataDialog = Dialog(this) 484 | gettingPlaceDataDialog.setContentView(R.layout.loading_place_details) 485 | 486 | val progressView = gettingPlaceDataDialog.findViewById(R.id.progressImageIV) 487 | Glide.with(this) 488 | .asGif() 489 | .load(R.raw.loading_spinner) 490 | .into(progressView) 491 | } 492 | 493 | override fun onRecantsItemSelected(savedItem: GroupStrategy.ListItem) { 494 | if (savedItem is GroupStrategy.GeneralItem) { 495 | /*tapped on place*/ 496 | getViewModel().requestPlaceDetails(savedItem.searchSelectedItem.placeId, apiKey!!) 497 | } else { 498 | /*tapped on group title*/ 499 | } 500 | 501 | } 502 | 503 | override fun onPlaceItemSelected(candidateItem: PredictionsItem?) { 504 | if (candidateItem != null) { 505 | getViewModel().requestPlaceDetails(candidateItem.placeId.toString(), apiKey = apiKey!!) 506 | } 507 | 508 | } 509 | 510 | private fun getViewModel() = viewModel 511 | } 512 | 513 | --------------------------------------------------------------------------------