├── vanillaplacepicker
├── .kotlintest
│ └── spec_failures
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── integer.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── font
│ │ │ │ └── opensans_reg.ttf
│ │ │ ├── values-ar
│ │ │ │ ├── integer.xml
│ │ │ │ └── strings.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ └── ic_current_location.png
│ │ │ ├── drawable-mdpi
│ │ │ │ └── ic_current_location.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── ic_current_location.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── ic_current_location.png
│ │ │ ├── drawable
│ │ │ │ ├── bg_white_corner_round.xml
│ │ │ │ ├── ic_arrow_back_black_24dp.xml
│ │ │ │ ├── ic_done_black_24dp.xml
│ │ │ │ ├── ic_location_on_red_24dp.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── drawable-anydpi
│ │ │ │ └── ic_current_location.xml
│ │ │ ├── values-hi
│ │ │ │ └── strings.xml
│ │ │ ├── values-es
│ │ │ │ └── strings.xml
│ │ │ ├── values-fr
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ ├── activity_vanilla_map.xml
│ │ │ │ └── custom_toolbar.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── vanillaplacepicker
│ │ │ │ ├── extenstion
│ │ │ │ ├── StringExts.kt
│ │ │ │ ├── ViewExts.kt
│ │ │ │ ├── AppCompatActivityExtension.kt
│ │ │ │ ├── AlertDialogExtension.kt
│ │ │ │ └── ContextWrapperExts.kt
│ │ │ │ ├── utils
│ │ │ │ ├── PickerType.kt
│ │ │ │ ├── PickerLanguage.kt
│ │ │ │ ├── ToastLength.kt
│ │ │ │ ├── MapType.kt
│ │ │ │ ├── BundleUtils.kt
│ │ │ │ ├── Logger.kt
│ │ │ │ ├── ToastUtils.kt
│ │ │ │ ├── KeyUtils.kt
│ │ │ │ ├── AutoCompleteUtils.kt
│ │ │ │ └── SharedPrefs.kt
│ │ │ │ ├── domain
│ │ │ │ └── common
│ │ │ │ │ └── SafeObserver.kt
│ │ │ │ ├── presentation
│ │ │ │ ├── builder
│ │ │ │ │ ├── SearchZoneRect.kt
│ │ │ │ │ ├── VanillaConfig.kt
│ │ │ │ │ └── VanillaPlacePicker.kt
│ │ │ │ ├── map
│ │ │ │ │ ├── VanillaMapViewModelFactory.kt
│ │ │ │ │ ├── VanillaMapViewModel.kt
│ │ │ │ │ └── VanillaMapActivity.kt
│ │ │ │ └── common
│ │ │ │ │ └── VanillaBaseViewModelActivity.kt
│ │ │ │ ├── data
│ │ │ │ ├── common
│ │ │ │ │ ├── BaseMapper.kt
│ │ │ │ │ ├── AddressMapperGoogleMap.kt
│ │ │ │ │ └── AutoCompleteAddressDetailsMapper.kt
│ │ │ │ ├── VanillaAddress.kt
│ │ │ │ └── GeocoderAddressResponse.kt
│ │ │ │ └── service
│ │ │ │ └── FetchAddressIntentService.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── vanillaplacepicker
│ │ │ ├── presentation
│ │ │ ├── common
│ │ │ │ ├── TestRulesListener.kt
│ │ │ │ └── TestRules.kt
│ │ │ └── map
│ │ │ │ └── VanillaMapViewModelTest.kt
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── vanillaplacepicker
│ │ └── ExampleInstrumentedTest.java
├── .gitignore
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── media
├── vanillaplacepicker-map.gif
└── vanillaplacepicker-autocomplete.gif
├── app
├── 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
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── raw
│ │ │ │ └── style_json.json
│ │ │ ├── drawable
│ │ │ │ ├── ic_undraw_address.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── ic_undraw_map.xml
│ │ │ │ └── ic_undraw_note_list.xml
│ │ │ └── layout
│ │ │ │ └── activity_sample.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── mindinventory
│ │ │ └── placepicker
│ │ │ └── activity
│ │ │ └── SampleActivity.kt
│ └── test
│ │ └── java
│ │ └── com
│ │ └── mindinventory
│ │ └── placepicker
│ │ └── ExampleUnitTest.kt
├── .gitignore
├── proguard-rules.pro
└── build.gradle
├── LICENSE
├── gradle.properties
├── gradlew.bat
├── .gitignore
├── README.md
└── gradlew
/vanillaplacepicker/.kotlintest/spec_failures:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':vanillaplacepicker'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/media/vanillaplacepicker-map.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/media/vanillaplacepicker-map.gif
--------------------------------------------------------------------------------
/media/vanillaplacepicker-autocomplete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/media/vanillaplacepicker-autocomplete.gif
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/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/Mindinventory/vanilla-place-picker/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/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/font/opensans_reg.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/vanillaplacepicker/src/main/res/font/opensans_reg.ttf
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values-ar/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 180
4 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable-hdpi/ic_current_location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/vanillaplacepicker/src/main/res/drawable-hdpi/ic_current_location.png
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable-mdpi/ic_current_location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/vanillaplacepicker/src/main/res/drawable-mdpi/ic_current_location.png
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable-xhdpi/ic_current_location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/vanillaplacepicker/src/main/res/drawable-xhdpi/ic_current_location.png
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable-xxhdpi/ic_current_location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mindinventory/vanilla-place-picker/HEAD/vanillaplacepicker/src/main/res/drawable-xxhdpi/ic_current_location.png
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/extenstion/StringExts.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.extenstion
2 |
3 | fun String?.isRequiredField(): Boolean {
4 | return this != null && isNotEmpty() && isNotBlank()
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/PickerType.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | enum class PickerType(val value: Int) {
4 | MAP_WITH_AUTO_COMPLETE(1), // Default
5 | AUTO_COMPLETE(2),
6 | MAP(3)
7 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/PickerLanguage.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | enum class PickerLanguage(val value: String) {
4 | ENGLISH("en"),
5 | FRENCH("fr"),
6 | HINDI("hi"),
7 | SPANISH("es"),
8 | ARABIC("ar")
9 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable/bg_white_corner_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 01 18:23:58 IST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/ToastLength.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import android.widget.Toast
4 |
5 | sealed class ToastLength(val value: Int) {
6 | object Short : ToastLength(Toast.LENGTH_SHORT)
7 | object Long : ToastLength(Toast.LENGTH_LONG)
8 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #2196F3
4 | #1976D2
5 | #D81B60
6 | #FFC107
7 |
8 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #2196F3
4 | #1976D2
5 | #D81B60
6 | #5B5B5B
7 |
8 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/extenstion/ViewExts.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.extenstion
2 |
3 | import android.view.View
4 | import androidx.core.view.isVisible
5 |
6 | fun View.hide() {
7 | this.isVisible = false
8 | }
9 |
10 | fun View.show() {
11 | this.isVisible = true
12 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/domain/common/SafeObserver.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.domain.common
2 |
3 | import androidx.lifecycle.Observer
4 |
5 | class SafeObserver(private val notifier: (T) -> Unit) : Observer {
6 | override fun onChanged(t: T?) {
7 | t?.let { notifier(t) }
8 | }
9 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/builder/SearchZoneRect.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.builder
2 |
3 | import android.os.Parcelable
4 | import com.google.android.gms.maps.model.LatLng
5 | import kotlinx.android.parcel.Parcelize
6 |
7 | @Parcelize
8 | data class SearchZoneRect(val lowerLeft: LatLng, val upperRight: LatLng) : Parcelable
--------------------------------------------------------------------------------
/vanillaplacepicker/src/test/java/com/vanillaplacepicker/presentation/common/TestRulesListener.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.common
2 |
3 | import io.kotlintest.Spec
4 | import io.kotlintest.extensions.TestListener
5 |
6 | object TestRulesListener : TestListener {
7 |
8 | override fun beforeSpec(spec: Spec) {
9 | instantTaskExecutorRule()
10 | }
11 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Vanilla Place Picker
3 | Place Picker\n(Search)
4 | Place Picker\n(Map)
5 | Selected Place
6 |
7 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/MapType.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import com.google.android.gms.maps.GoogleMap
4 |
5 | enum class MapType(val value: Int) {
6 | NORMAL(GoogleMap.MAP_TYPE_NORMAL),
7 | SATELLITE(GoogleMap.MAP_TYPE_SATELLITE),
8 | TERRAIN(GoogleMap.MAP_TYPE_TERRAIN),
9 | HYBRID(GoogleMap.MAP_TYPE_HYBRID),
10 | NONE(GoogleMap.MAP_TYPE_NONE)
11 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable/ic_arrow_back_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable/ic_done_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/data/common/BaseMapper.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.data.common
2 |
3 | abstract class BaseMapper {
4 |
5 | fun apply(oldItem: T): R {
6 | return map(oldItem)
7 | }
8 |
9 | fun apply(oldItem: List): List {
10 | return oldItem.map {
11 | apply(it)
12 | }
13 | }
14 |
15 | protected abstract fun map(oldItem: T): R
16 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/mindinventory/placepicker/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.mindinventory.placepicker
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Generated files
9 | bin/
10 | gen/
11 |
12 | # Gradle files
13 | .gradle/
14 | /*/build/
15 | gradle/
16 | gradlew
17 | gradlew.bat
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # OSX files
23 | .DS_Store
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio
29 | *.iml
30 | .idea
31 | .gradle
32 | build/
33 | /captures
34 | release/
35 | /app/schemas
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/BundleUtils.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import android.content.Context
4 | import android.content.pm.PackageManager
5 | import android.os.Bundle
6 |
7 | object BundleUtils {
8 | // Get MetaData from AndroidManifest file of given context
9 | fun getMetaData(context: Context): Bundle {
10 | return context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).metaData
11 | }
12 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Generated files
9 | bin/
10 | gen/
11 |
12 | # Gradle files
13 | .gradle/
14 | /*/build/
15 | gradle/
16 | gradlew
17 | gradlew.bat
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # OSX files
23 | .DS_Store
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio
29 | *.iml
30 | .idea
31 | .gradle
32 | build/
33 | /captures
34 | release/
35 | /app/schemas
--------------------------------------------------------------------------------
/vanillaplacepicker/src/test/java/com/vanillaplacepicker/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker;
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 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/map/VanillaMapViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.map
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import com.vanillaplacepicker.utils.SharedPrefs
6 |
7 | class VanillaMapViewModelFactory(private val sharedPrefs: SharedPrefs) : ViewModelProvider.Factory {
8 | override fun create(modelClass: Class): T {
9 | return modelClass.cast(VanillaMapViewModel(sharedPrefs)) as T
10 | }
11 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable/ic_location_on_red_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/data/VanillaAddress.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.data
2 |
3 | import java.io.Serializable
4 |
5 | data class VanillaAddress(
6 | var formattedAddress: String? = null,
7 | var name: String? = null,
8 | var placeId: String? = null,
9 | var latitude: Double? = null,
10 | var longitude: Double? = null,
11 | var locality: String? = null,
12 | var subLocality: String? = null,
13 | var postalCode: String? = null,
14 | var countryCode: String? = null,
15 | var countryName: String? = null
16 | ) : Serializable
--------------------------------------------------------------------------------
/vanillaplacepicker/src/test/java/com/vanillaplacepicker/presentation/common/TestRules.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.common
2 |
3 | import androidx.arch.core.executor.ArchTaskExecutor
4 | import androidx.arch.core.executor.TaskExecutor
5 |
6 | fun instantTaskExecutorRule() {
7 | ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
8 | override fun executeOnDiskIO(runnable: Runnable) {
9 | runnable.run()
10 | }
11 |
12 | override fun postToMainThread(runnable: Runnable) {
13 | runnable.run()
14 | }
15 |
16 | override fun isMainThread(): Boolean = true
17 | })
18 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/Logger.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import android.util.Log
4 | import com.vanillaplacepicker.BuildConfig
5 |
6 | object Logger {
7 | fun d(tag: String, message: Any?) {
8 | if (BuildConfig.DEBUG) {
9 | Log.d(tag, message.toString())
10 | }
11 | }
12 |
13 | fun e(tag: String, message: Any?) {
14 | if (BuildConfig.DEBUG) {
15 | Log.e(tag, message.toString())
16 | }
17 | }
18 |
19 | fun i(tag: String, message: Any?) {
20 | if (BuildConfig.DEBUG) {
21 | Log.i(tag, message.toString())
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/drawable-anydpi/ic_current_location.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/extenstion/AppCompatActivityExtension.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.extenstion
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.provider.Settings
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | /**
9 | * this function will help to open app setting using Intent.
10 | */
11 | fun AppCompatActivity.openAppSetting() {
12 | val intent = Intent()
13 | intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
14 | val uri = Uri.fromParts("package", packageName, null)
15 | intent.data = uri
16 | startActivity(intent)
17 | }
18 |
19 | fun AppCompatActivity.hasExtra(key: String): Boolean {
20 | return this.intent.hasExtra(key)
21 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/data/GeocoderAddressResponse.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.data
2 |
3 | import android.os.Bundle
4 | import java.io.Serializable
5 |
6 | data class GeoCoderAddressResponse(
7 | var addressLine: String?,
8 | var featureName: String?,
9 | var adminArea: String?,
10 | var subAdminArea: String?,
11 | var locality: String?,
12 | var thoroughfare: String?,
13 | var postalCode: String?,
14 | var countryCode: String?,
15 | var countryName: String?,
16 | var hasLatitude: Boolean?,
17 | var latitude: Double?,
18 | var hasLongitude: Boolean?,
19 | var longitude: Double?,
20 | var phone: String?,
21 | var url: String?,
22 | var extras: Bundle?
23 | ) : Serializable
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/ToastUtils.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import android.content.Context
4 | import android.widget.Toast
5 |
6 | object ToastUtils {
7 | private lateinit var toast: Toast
8 |
9 | fun showToast(context: Context, message: String?, duration: ToastLength = ToastLength.Long) {
10 | if (::toast.isInitialized)
11 | toast.cancel()
12 | message?.let {
13 | toast = Toast.makeText(context, message, duration.value)
14 | toast.show()
15 | }
16 | }
17 |
18 | fun showToast(context: Context, message: Int, duration: ToastLength = ToastLength.Long) {
19 | if (::toast.isInitialized) toast.cancel()
20 | toast = Toast.makeText(context, message, duration.value)
21 | toast.show()
22 | }
23 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/vanillaplacepicker/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 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/androidTest/java/com/vanillaplacepicker/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 |
9 | import static org.junit.Assert.*;
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * @see Testing documentation
15 | */
16 | @RunWith(AndroidJUnit4.class)
17 | public class ExampleInstrumentedTest {
18 | @Test
19 | public void useAppContext() {
20 | // Context of the app under test.
21 | Context appContext = InstrumentationRegistry.getTargetContext();
22 |
23 | assertEquals("com.miplacepicker.test", appContext.getPackageName());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/map/VanillaMapViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.map
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.google.android.gms.maps.model.LatLng
6 | import com.vanillaplacepicker.utils.SharedPrefs
7 |
8 | class VanillaMapViewModel(private val sharedPrefs: SharedPrefs) : ViewModel() {
9 |
10 | var latLngLiveData = MutableLiveData()
11 |
12 | fun fetchSavedLocation() {
13 | val latitude = sharedPrefs.deviceLatitude.toDouble()
14 | val longitude = sharedPrefs.deviceLongitude.toDouble()
15 | latLngLiveData.postValue(LatLng(latitude, longitude))
16 | }
17 |
18 | fun saveLatLngToSharedPref(latitude: Double, longitude: Double) {
19 | sharedPrefs.deviceLatitude = latitude.toFloat()
20 | sharedPrefs.deviceLongitude = longitude.toFloat()
21 | }
22 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/extenstion/AlertDialogExtension.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.extenstion
2 |
3 | import androidx.appcompat.app.AlertDialog
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.vanillaplacepicker.R
6 |
7 | fun AppCompatActivity.showAlertDialog(
8 | message: Int = 0, title: Int = 0,
9 | positiveBtnText: Int = R.string.ok,
10 | negativeBtnText: Int = R.string.cancel,
11 | accepted: () -> Unit = {},
12 | rejected: () -> Unit = {}
13 | ) {
14 | AlertDialog.Builder(this)
15 | .setTitle(title)
16 | .setMessage(message)
17 | .setPositiveButton(positiveBtnText) { dialog, _ ->
18 | accepted.invoke()
19 | dialog.dismiss()
20 | }.setNegativeButton(negativeBtnText) { dialog, _ ->
21 | rejected.invoke()
22 | dialog.dismiss()
23 | }.show()
24 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/data/common/AddressMapperGoogleMap.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.data.common
2 |
3 | import com.vanillaplacepicker.data.GeoCoderAddressResponse
4 | import com.vanillaplacepicker.data.VanillaAddress
5 |
6 | object AddressMapperGoogleMap : BaseMapper() {
7 |
8 | override fun map(oldItem: GeoCoderAddressResponse): VanillaAddress {
9 | return VanillaAddress().apply {
10 | this.formattedAddress = oldItem.addressLine
11 | this.name = oldItem.featureName
12 | this.locality = oldItem.locality
13 | this.subLocality = oldItem.subAdminArea
14 | this.latitude = oldItem.latitude
15 | this.longitude = oldItem.longitude
16 | this.postalCode = oldItem.postalCode
17 | this.countryCode = oldItem.countryCode
18 | this.countryName = oldItem.countryName
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/KeyUtils.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | object KeyUtils {
4 | const val SUCCESS_RESULT = 0
5 | const val FAILURE_RESULT = 1
6 | private const val PACKAGE_NAME = "com.google.android.gms.location.sample.locationaddress"
7 | const val RECEIVER = "$PACKAGE_NAME.RECEIVER"
8 | const val RESULT_DATA_KEY = "$PACKAGE_NAME.RESULT_DATA_KEY"
9 | const val RESULT_MESSAGE_KEY = "$PACKAGE_NAME.RESULT_MESSAGE_KEY"
10 | const val LOCATION_DATA_EXTRA = "$PACKAGE_NAME.LOCATION_DATA_EXTRA"
11 | const val DEFAULT_ZOOM_LEVEL = 18f
12 | const val GOOGLE_MAP_CAMERA_ANIMATE_DURATION = 2000
13 | const val REQUEST_PERMISSIONS_REQUEST_CODE = 102
14 | const val LOCATION_UPDATE_INTERVAL = 3000L
15 | const val DEFAULT_LOCATION = 0.0
16 | const val DEFAULT_STYLE_JSON_RESID = 0
17 | const val DEFAULT_FETCH_LOCATION_INTERVAL = 5000L
18 | const val SELECTED_PLACE = "place"
19 | const val EXTRA_CONFIG = "extraConfig"
20 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mindinventory
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.
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/builder/VanillaConfig.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.builder
2 |
3 | import android.os.Parcelable
4 | import androidx.annotation.DrawableRes
5 | import com.vanillaplacepicker.utils.KeyUtils
6 | import com.vanillaplacepicker.utils.MapType
7 | import com.vanillaplacepicker.utils.PickerType
8 | import kotlinx.parcelize.Parcelize
9 |
10 | @Parcelize
11 | data class VanillaConfig(
12 | var apiKey: String? = "",
13 | var pickerType: PickerType = PickerType.MAP_WITH_AUTO_COMPLETE,
14 | var country: String? = null,
15 | var latitude: Double = KeyUtils.DEFAULT_LOCATION,
16 | var longitude: Double = KeyUtils.DEFAULT_LOCATION,
17 | var radius: Int? = null,
18 | var language: String? = null,
19 | var types: String? = null,
20 | var zoneRect: SearchZoneRect? = null,
21 | var enableSatelliteView: Boolean = false,
22 | var mapStyleJSONResId: Int = KeyUtils.DEFAULT_STYLE_JSON_RESID,
23 | var mapType: MapType = MapType.NORMAL,
24 | var enableShowMapAfterSearchResult: Boolean = false,
25 | @DrawableRes var mapPinDrawable: Int? = null
26 | ) : Parcelable
--------------------------------------------------------------------------------
/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 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/data/common/AutoCompleteAddressDetailsMapper.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.data.common
2 |
3 | import com.google.android.libraries.places.api.model.Place
4 | import com.vanillaplacepicker.data.VanillaAddress
5 |
6 | object AutoCompleteAddressDetailsMapper : BaseMapper() {
7 |
8 | override fun map(oldItem: Place): VanillaAddress {
9 | return VanillaAddress().apply {
10 | this.formattedAddress = oldItem.address
11 | this.name = oldItem.name
12 | this.latitude = oldItem.latLng?.latitude
13 | this.longitude = oldItem.latLng?.longitude
14 | oldItem.addressComponents?.asList()?.forEach {
15 | when {
16 | it.types.contains("locality") -> this.locality = it.name
17 | it.types.contains("sublocality_level_1") -> this.subLocality = it.name
18 | it.types.contains("postal_code") -> this.postalCode = it.name
19 | it.types.contains("country") -> {
20 | this.countryCode = it.shortName
21 | this.countryName = it.name
22 | }
23 | }
24 | }
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/common/VanillaBaseViewModelActivity.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.common
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import androidx.annotation.CallSuper
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.lifecycle.ViewModel
8 | import androidx.viewbinding.ViewBinding
9 |
10 | abstract class VanillaBaseViewModelActivity : AppCompatActivity() {
11 |
12 | private var _binding: VB? = null
13 | protected val binding get() = _binding!!
14 | protected val viewModel by lazy { buildViewModel() }
15 |
16 | protected open fun getBundle() {
17 | }
18 |
19 | protected abstract fun buildViewModel(): T
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | _binding = inflateLayout(layoutInflater)
24 | setContentView(binding.root)
25 | getBundle()
26 | initViews()
27 | initLiveDataObservers()
28 | }
29 |
30 | abstract fun inflateLayout(layoutInflater: LayoutInflater): VB
31 |
32 | @CallSuper
33 | protected open fun initLiveDataObservers() = Unit
34 |
35 | @CallSuper
36 | protected open fun initViews() {
37 | }
38 |
39 | override fun onDestroy() {
40 | super.onDestroy()
41 | _binding = null
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-android-extensions'
5 | }
6 |
7 | android {
8 | compileSdk 33
9 | defaultConfig {
10 | applicationId "com.mindinventory.placepicker"
11 | minSdk 21
12 | targetSdk 33
13 | versionCode 1
14 | versionName "1.0"
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | vectorDrawables.useSupportLibrary = true
17 | multiDexEnabled true
18 |
19 | // Load credentials.
20 | def properties = new Properties()
21 | file("../local.properties").withInputStream { properties.load(it) }
22 |
23 | // Share the key with your `AndroidManifest.xml`
24 | manifestPlaceholders = [ googleMapsApiKey:"${properties.getProperty('google.maps_api_key')}"]
25 | }
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 | buildFeatures {
33 | viewBinding true
34 | }
35 | }
36 |
37 | dependencies {
38 | implementation fileTree(dir: 'libs', include: ['*.jar'])
39 | implementation "androidx.appcompat:appcompat:1.5.1"
40 | implementation "androidx.cardview:cardview:1.0.0"
41 |
42 | testImplementation 'junit:junit:4.13.2'
43 | implementation project(path: ':vanillaplacepicker')
44 | implementation 'androidx.multidex:multidex:2.0.1'
45 | }
46 |
47 | apply plugin: 'com.google.gms.google-services'
48 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/extenstion/ContextWrapperExts.kt:
--------------------------------------------------------------------------------
1 | import android.annotation.TargetApi
2 | import android.content.ContextWrapper
3 | import android.os.Build
4 | import java.util.*
5 |
6 | @Suppress("DEPRECATION")
7 | fun ContextWrapper.wrap(language: String): ContextWrapper {
8 | val config = baseContext.resources.configuration
9 | val sysLocale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
10 | this.getSystemLocale()
11 | } else {
12 | this.getSystemLocaleLegacy()
13 | }
14 |
15 | if (language.isNotEmpty() && sysLocale.language != language) {
16 | val locale = Locale(language)
17 | Locale.setDefault(locale)
18 |
19 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
20 | this.setSystemLocale(locale)
21 | } else {
22 | this.setSystemLocaleLegacy(locale)
23 | }
24 | }
25 | baseContext.resources.updateConfiguration(config, baseContext.resources.displayMetrics)
26 | return ContextWrapper(baseContext)
27 | }
28 |
29 | @Suppress("DEPRECATION")
30 | fun ContextWrapper.getSystemLocaleLegacy(): Locale {
31 | val config = baseContext.resources.configuration
32 | return config.locale
33 | }
34 |
35 | @TargetApi(Build.VERSION_CODES.N)
36 | fun ContextWrapper.getSystemLocale(): Locale {
37 | val config = baseContext.resources.configuration
38 | return config.locales[0]
39 | }
40 |
41 |
42 | @Suppress("DEPRECATION")
43 | fun ContextWrapper.setSystemLocaleLegacy(locale: Locale) {
44 | val config = baseContext.resources.configuration
45 | config.locale = locale
46 | }
47 |
48 | @TargetApi(Build.VERSION_CODES.N)
49 | fun ContextWrapper.setSystemLocale(locale: Locale) {
50 | val config = baseContext.resources.configuration
51 | config.setLocale(locale)
52 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values-ar/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | لم يتم تقديم بيانات الموقع
3 | عذرا ، الخدمة غير متوفرة
4 | خط العرض أو خط الطول غير صالح
5 | آسف ، لم يتم العثور على عنوان
6 | تم العثور على العنوان
7 | لم يتم العثور على اقتراحات.
8 | وافق المستخدم على إجراء تغييرات إعدادات الموقع المطلوبة.
9 | يختار المستخدم عدم إجراء تغييرات إعدادات الموقع المطلوبة.
10 |
11 | تم إلغاء تفاعل المستخدم.
12 | راضون جميع إعدادات الموقع.
13 | إعدادات الموقع غير كافية ولا يمكن إصلاحها هنا. إصلاح في الإعدادات.
14 | مفقود إذن
15 | يرجى السماح لنا بالوصول إلى إذنك لخدمتك بشكل أفضل.
16 | حسنا
17 | إلغاء
18 | الإذن
19 |
20 | الإذن…
21 |
22 |
23 | أدخل عنوان أو مكان
24 | نتائج البحث تظهر هنا
25 | حدث خطأ ما. أعد المحاولة من فضلك.
26 | VanillaPlacePicker
27 |
28 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values-hi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | कोई स्थान डेटा प्रदान नहीं किया गया
3 | क्षमा करें, सेवा उपलब्ध नहीं है
4 | उपयोगकर्ता सहभागिता रद्द कर दी गई थी।
5 | क्षमा करें, कोई पता नहीं मिला
6 | पता मिला
7 | कोई सुझाव नहीं मिला।
8 | उपयोगकर्ता आवश्यक स्थान सेटिंग्स परिवर्तन करने
9 | के लिए सहमत हुए।
10 |
11 | उपयोगकर्ता ने आवश्यक स्थान सेटिंग परिवर्तन
12 | नहीं करना चुना।
13 |
14 | उपयोगकर्ता सहभागिता रद्द कर दी गई थी।
15 | सभी स्थान सेटिंग संतुष्ट हैं।
16 | स्थान सेटिंग अपर्याप्त हैं और यहां तय नहीं
17 | की जा सकती हैं। सेटिंग्स में ठीक करें।
18 |
19 | गुम अनुमति
20 | कृपया हमें आपकी सेवा के लिए अनुमति दें ताकि आप बेहतर सेवा कर सकें।
21 | ठीक
22 | रद्द करना
23 | अनुमति
24 |
25 |
26 |
27 | खोज कर…
28 |
29 |
30 | एक पता या जगह दर्ज करें
31 | खोज परिणाम यहां दिखाई देते हैं
32 | कुछ गलत हो गया। कृपया पुन: प्रयास करें।
33 | VanillaPlacePicker
34 |
35 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/AutoCompleteUtils.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import com.google.android.libraries.places.api.Places
6 | import com.google.android.libraries.places.api.model.Place
7 | import com.google.android.libraries.places.api.model.RectangularBounds
8 | import com.google.android.libraries.places.widget.Autocomplete
9 | import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
10 | import com.vanillaplacepicker.presentation.builder.VanillaConfig
11 |
12 | object AutoCompleteUtils {
13 | private val fields = listOf(
14 | Place.Field.ADDRESS,
15 | Place.Field.ADDRESS_COMPONENTS,
16 | Place.Field.ID,
17 | Place.Field.LAT_LNG,
18 | Place.Field.NAME,
19 | Place.Field.OPENING_HOURS,
20 | Place.Field.PHONE_NUMBER,
21 | Place.Field.PHOTO_METADATAS,
22 | Place.Field.PLUS_CODE,
23 | Place.Field.PRICE_LEVEL,
24 | Place.Field.RATING,
25 | Place.Field.TYPES,
26 | Place.Field.USER_RATINGS_TOTAL,
27 | Place.Field.UTC_OFFSET,
28 | Place.Field.VIEWPORT,
29 | Place.Field.WEBSITE_URI
30 | )
31 |
32 | fun getAutoCompleteIntent(context: Context, vanillaConfig: VanillaConfig): Intent {
33 |
34 | if (!Places.isInitialized())
35 | vanillaConfig.apiKey?.let { Places.initialize(context.applicationContext, it) }
36 |
37 | val autocomplete = Autocomplete.IntentBuilder(
38 | AutocompleteActivityMode.FULLSCREEN,
39 | fields
40 | )
41 |
42 | if (!vanillaConfig.country.isNullOrBlank()) {
43 | autocomplete.setCountry(vanillaConfig.country)
44 | }
45 | vanillaConfig.zoneRect?.let {
46 | autocomplete.setLocationRestriction(
47 | RectangularBounds.newInstance(
48 | it.lowerLeft,
49 | it.upperRight
50 | )
51 | )
52 | }
53 | return autocomplete.build(context)
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | No se han proporcionado datos de ubicación
3 | Lo sentimos, el servicio no está disponible.
4 | La interacción del usuario fue cancelada.
5 | Lo sentimos, no se encontró la dirección
6 | Dirección encontrada
7 | No se encontró ninguna sugerencia.
8 | El usuario acordó realizar los cambios de
9 | configuración de ubicación requeridos.
10 |
11 | El usuario optó por no realizar los cambios
12 | de configuración de ubicación requeridos.
13 |
14 | La interacción del usuario fue cancelada.
15 | Todos los ajustes de ubicación están satisfechos.
16 | La configuración de la ubicación es
17 | inadecuada y no se puede arreglar aquí. Arreglo en Configuraciones.
18 |
19 | Permiso faltante
20 | Permítanos tener acceso a su permiso para servirle mejor.
21 | DE ACUERDO
22 | Cancelar
23 | Permiso
24 |
25 |
26 |
27 | Buscando…
28 |
29 |
30 | Introduce una dirección o lugar
31 | Los resultados de búsqueda aparecen aquí
32 | Algo salió mal. Por favor, vuelva a intentarlo.
33 | VanillaPlacePicker
34 |
35 |
--------------------------------------------------------------------------------
/vanillaplacepicker/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'kotlin-parcelize'
5 | }
6 |
7 | group='com.github.Mindinventory'
8 |
9 | android {
10 | compileSdk 33
11 |
12 | defaultConfig {
13 | minSdk 21
14 | targetSdk 33
15 | versionCode 16
16 | versionName "0.3.1"
17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
18 | vectorDrawables.useSupportLibrary = true
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | compileOptions {
29 | sourceCompatibility = 1.8
30 | targetCompatibility = 1.8
31 | }
32 |
33 | kotlinOptions {
34 | jvmTarget = "1.8"
35 | }
36 |
37 | testOptions {
38 | unitTests.returnDefaultValues = true
39 | }
40 |
41 | buildFeatures {
42 | viewBinding true
43 | }
44 | }
45 |
46 | dependencies {
47 | implementation fileTree(dir: 'libs', include: ['*.jar'])
48 | implementation 'androidx.appcompat:appcompat:1.5.1'
49 | implementation 'androidx.core:core-ktx:1.9.0'
50 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
51 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
52 | implementation "androidx.recyclerview:recyclerview:1.2.1"
53 | testImplementation 'junit:junit:4.13.2'
54 |
55 | // Location
56 | api 'com.google.android.gms:play-services-location:21.0.1'
57 | api 'com.google.android.gms:play-services-maps:18.1.0'
58 |
59 | // Google material
60 | implementation 'com.google.android.material:material:1.7.0'
61 |
62 | implementation "com.google.android.libraries.places:places:3.0.0"
63 |
64 | //unittest
65 | testImplementation 'org.mockito:mockito-core:4.0.0'
66 | testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
67 | testImplementation 'org.mockito:mockito-inline:3.5.13'
68 | testImplementation 'android.arch.core:core-testing:1.1.1'
69 | testImplementation 'io.kotlintest:kotlintest-runner-junit5:3.3.2'
70 | }
71 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Aucune donnée de localisation fournie
3 | Désolé, le service n\'est pas disponible
4 | L\'interaction de l\'utilisateur a été annulée.
5 | Désolé, aucune adresse trouvée
6 | Adresse trouvée
7 | Aucune suggestion trouvée.
8 | L\'utilisateur a accepté de modifier les
9 | paramètres d\'emplacement requis.
10 |
11 | L\'utilisateur a choisi de ne pas modifier
12 | les paramètres d\'emplacement requis.
13 |
14 | L\'interaction de l\'utilisateur a été annulée.
15 | Tous les paramètres de localisation sont satisfaits.
16 | Les paramètres de localisation sont
17 | inadéquats et ne peuvent pas être corrigés ici. Correction dans les paramètres.
18 |
19 | Permission manquante
20 | S\'il vous plaît, permettez-nous d\'accéder à votre permission pour mieux
21 | vous servir.
22 |
23 | D\'accord
24 | Annuler
25 | Autorisation
26 |
27 |
28 | Recherche…
29 |
30 |
31 | Entrez une adresse ou un lieu
32 | Les résultats de recherche apparaissent ici
33 | Une erreur s\'est produite. Veuillez réessayer.
34 | VanillaPlacePicker
35 |
36 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | No location data provided
3 | Sorry, the service is not available
4 | Invalid latitude or longitude used
5 | Sorry, no address found
6 | Address found
7 | No suggestion found.
8 | User agreed to make required location settings
9 | changes.
10 |
11 | User choose not to make required location
12 | settings changes.
13 |
14 | User interaction was cancelled.
15 | All location settings are satisfied.
16 | Location settings are inadequate and cannotbe fixed here. Fix in Settings.
17 | Missing Permission
18 | Please allow us to access your permission to serve you better.
19 | OK
20 | Cancel
21 | Permission
22 |
23 | Searching…
24 |
25 |
26 | Enter an address or place
27 | Search results appear here
28 |
29 | Location settings are not satisfied. Attempting to upgrade
30 | location settings
31 |
32 | PendingIntent unable to execute request.
33 | Something went wrong, please try again.
34 | VanillaPlacePicker
35 |
36 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/utils/SharedPrefs.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.utils
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 |
6 | class SharedPrefs(context: Context) {
7 | private val sharedPreferences: SharedPreferences =
8 | context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
9 |
10 | var deviceLatitude: Float
11 | set(value) = put(PREF_DEVICE_LATITUDE, value)
12 | get() = get(PREF_DEVICE_LATITUDE, Float::class.java)
13 |
14 | var deviceLongitude: Float
15 | set(value) = put(PREF_DEVICE_LONGITUDE, value)
16 | get() = get(PREF_DEVICE_LONGITUDE, Float::class.java)
17 |
18 | @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
19 | private fun get(key: String, clazz: Class): T =
20 | when (clazz) {
21 | String::class.java -> sharedPreferences.getString(key, "")
22 | Boolean::class.java -> sharedPreferences.getBoolean(key, false)
23 | Float::class.java -> sharedPreferences.getFloat(key, 0f)
24 | Double::class.java -> sharedPreferences.getFloat(key, 0f)
25 | Int::class.java -> sharedPreferences.getInt(key, -1)
26 | Long::class.java -> sharedPreferences.getLong(key, -1L)
27 | else -> null
28 | } as T
29 |
30 | private fun put(key: String, data: T) {
31 | val editor = sharedPreferences.edit()
32 | when (data) {
33 | is String -> editor.putString(key, data)
34 | is Boolean -> editor.putBoolean(key, data)
35 | is Float -> editor.putFloat(key, data)
36 | is Double -> editor.putFloat(key, data.toFloat())
37 | is Int -> editor.putInt(key, data)
38 | is Long -> editor.putLong(key, data)
39 | }
40 | editor.apply()
41 | }
42 |
43 | fun clear() {
44 | sharedPreferences.edit().clear().apply()
45 | }
46 |
47 | companion object {
48 | const val PREFS_NAME = "KFCSharedPreferences"
49 | private const val PREFIX = "kfc_"
50 | const val PREF_SESSION_ACCESS_TOKEN = PREFIX + "access_token"
51 | const val PREF_DEVICE_LATITUDE = PREFIX + "device_latitude"
52 | const val PREF_DEVICE_LONGITUDE = PREFIX + "device_longitude"
53 | }
54 | }
--------------------------------------------------------------------------------
/vanillaplacepicker/src/test/java/com/vanillaplacepicker/presentation/map/VanillaMapViewModelTest.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.map
2 |
3 | import androidx.lifecycle.Observer
4 | import com.google.android.gms.maps.model.LatLng
5 | import com.nhaarman.mockitokotlin2.argumentCaptor
6 | import com.nhaarman.mockitokotlin2.mock
7 | import com.nhaarman.mockitokotlin2.times
8 | import com.nhaarman.mockitokotlin2.whenever
9 | import com.vanillaplacepicker.presentation.common.TestRulesListener
10 | import com.vanillaplacepicker.utils.SharedPrefs
11 | import io.kotlintest.extensions.TestListener
12 | import io.kotlintest.shouldBe
13 | import io.kotlintest.specs.BehaviorSpec
14 | import org.mockito.Mockito
15 |
16 | class VanillaMapViewModelTest : BehaviorSpec() {
17 |
18 | private val latitude = 23.058759689331055
19 | private val longitude = 72.53572845458984
20 | private val latLng = LatLng(latitude, longitude)
21 |
22 | //output value
23 | private val successLatLng = latLng
24 |
25 | //mock dependencies
26 | private val sharedPrefs = mock()
27 | private val vanillaMapViewModel by lazy { VanillaMapViewModel(sharedPrefs) }
28 | private var vanillaMapObserver = mock>()
29 |
30 | override fun listeners(): List = listOf(TestRulesListener)
31 |
32 | init {
33 | fetchSavedLocationSuccess()
34 | }
35 |
36 | private fun fetchSavedLocationSuccess() {
37 | Given("Fetch saved location") {
38 | When("LatLng Available") {
39 | stubFetchSavedLocationSuccess()
40 | vanillaMapViewModel.latLngLiveData.observeForever(vanillaMapObserver)
41 | vanillaMapViewModel.fetchSavedLocation()
42 | Then("Fetch saved location success") {
43 | argumentCaptor().apply {
44 | Mockito.verify(vanillaMapObserver, times(1)).onChanged(capture())
45 | LatLng(firstValue.latitude, firstValue.longitude) shouldBe successLatLng
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | //stubbing
53 | private fun stubFetchSavedLocationSuccess() {
54 | whenever(sharedPrefs.deviceLatitude).thenReturn(latitude.toFloat())
55 | whenever(sharedPrefs.deviceLongitude).thenReturn(longitude.toFloat())
56 | }
57 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/layout/activity_vanilla_map.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
28 |
31 |
32 |
44 |
45 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mindinventory/placepicker/activity/SampleActivity.kt:
--------------------------------------------------------------------------------
1 | package com.mindinventory.placepicker.activity
2 |
3 | import android.app.Activity
4 | import android.os.Bundle
5 | import android.view.View
6 | import androidx.activity.result.contract.ActivityResultContracts
7 | import androidx.appcompat.app.AppCompatActivity
8 | import com.mindinventory.placepicker.R
9 | import com.mindinventory.placepicker.databinding.ActivitySampleBinding
10 | import com.vanillaplacepicker.extenstion.show
11 | import com.vanillaplacepicker.presentation.builder.VanillaPlacePicker
12 | import com.vanillaplacepicker.utils.MapType
13 | import com.vanillaplacepicker.utils.PickerLanguage
14 | import com.vanillaplacepicker.utils.PickerType
15 |
16 | class SampleActivity : AppCompatActivity(), View.OnClickListener {
17 |
18 | private var _binding: ActivitySampleBinding? = null
19 | private val binding get() = _binding!!
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | _binding = ActivitySampleBinding.inflate(layoutInflater)
24 | setContentView(binding.root)
25 |
26 | initClickListener()
27 | }
28 |
29 | private fun initClickListener() {
30 | binding.cardviewPlacePickerSearch.setOnClickListener(this)
31 | binding.cardviewPlacePickerMap.setOnClickListener(this)
32 | }
33 |
34 | private var placePickerResultLauncher =
35 | registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
36 | if (result.resultCode == Activity.RESULT_OK && result.data != null) {
37 | val vanillaAddress = VanillaPlacePicker.getPlaceResult(result.data)
38 | vanillaAddress?.let {
39 | binding.cardviewSelectedPlace.show()
40 | binding.tvSelectedPlace.text = it.formattedAddress
41 | }
42 | }
43 | }
44 |
45 | override fun onClick(v: View?) {
46 | when (v?.id) {
47 | R.id.cardviewPlacePickerSearch -> {
48 | val intent = VanillaPlacePicker.Builder(this)
49 | .with(PickerType.AUTO_COMPLETE)
50 | .withLocation(23.0710, 72.5181)
51 | .setPickerLanguage(PickerLanguage.ENGLISH)
52 | .build()
53 | placePickerResultLauncher.launch(intent)
54 | }
55 |
56 | R.id.cardviewPlacePickerMap -> {
57 | val intent = VanillaPlacePicker.Builder(this)
58 | .with(PickerType.MAP_WITH_AUTO_COMPLETE)
59 | .setMapType(MapType.SATELLITE)
60 | .withLocation(23.0710, 72.5181)
61 | .setPickerLanguage(PickerLanguage.ENGLISH)
62 | .enableShowMapAfterSearchResult(true)
63 | .build()
64 | placePickerResultLauncher.launch(intent)
65 | }
66 | }
67 | }
68 |
69 | override fun onDestroy() {
70 | super.onDestroy()
71 | _binding = null
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/res/layout/custom_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
19 |
20 |
31 |
32 |
43 |
44 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/style_json.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "featureType": "all",
4 | "elementType": "geometry",
5 | "stylers": [
6 | {
7 | "color": "#242f3e"
8 | }
9 | ]
10 | },
11 | {
12 | "featureType": "all",
13 | "elementType": "labels.text.stroke",
14 | "stylers": [
15 | {
16 | "lightness": -80
17 | }
18 | ]
19 | },
20 | {
21 | "featureType": "administrative",
22 | "elementType": "labels.text.fill",
23 | "stylers": [
24 | {
25 | "color": "#746855"
26 | }
27 | ]
28 | },
29 | {
30 | "featureType": "administrative.locality",
31 | "elementType": "labels.text.fill",
32 | "stylers": [
33 | {
34 | "color": "#d59563"
35 | }
36 | ]
37 | },
38 | {
39 | "featureType": "poi",
40 | "elementType": "labels.text.fill",
41 | "stylers": [
42 | {
43 | "color": "#d59563"
44 | }
45 | ]
46 | },
47 | {
48 | "featureType": "poi.park",
49 | "elementType": "geometry",
50 | "stylers": [
51 | {
52 | "color": "#263c3f"
53 | }
54 | ]
55 | },
56 | {
57 | "featureType": "poi.park",
58 | "elementType": "labels.text.fill",
59 | "stylers": [
60 | {
61 | "color": "#6b9a76"
62 | }
63 | ]
64 | },
65 | {
66 | "featureType": "road",
67 | "elementType": "geometry.fill",
68 | "stylers": [
69 | {
70 | "color": "#2b3544"
71 | }
72 | ]
73 | },
74 | {
75 | "featureType": "road",
76 | "elementType": "labels.text.fill",
77 | "stylers": [
78 | {
79 | "color": "#9ca5b3"
80 | }
81 | ]
82 | },
83 | {
84 | "featureType": "road.arterial",
85 | "elementType": "geometry.fill",
86 | "stylers": [
87 | {
88 | "color": "#38414e"
89 | }
90 | ]
91 | },
92 | {
93 | "featureType": "road.arterial",
94 | "elementType": "geometry.stroke",
95 | "stylers": [
96 | {
97 | "color": "#212a37"
98 | }
99 | ]
100 | },
101 | {
102 | "featureType": "road.highway",
103 | "elementType": "geometry.fill",
104 | "stylers": [
105 | {
106 | "color": "#746855"
107 | }
108 | ]
109 | },
110 | {
111 | "featureType": "road.highway",
112 | "elementType": "geometry.stroke",
113 | "stylers": [
114 | {
115 | "color": "#1f2835"
116 | }
117 | ]
118 | },
119 | {
120 | "featureType": "road.highway",
121 | "elementType": "labels.text.fill",
122 | "stylers": [
123 | {
124 | "color": "#f3d19c"
125 | }
126 | ]
127 | },
128 | {
129 | "featureType": "road.local",
130 | "elementType": "geometry.fill",
131 | "stylers": [
132 | {
133 | "color": "#38414e"
134 | }
135 | ]
136 | },
137 | {
138 | "featureType": "road.local",
139 | "elementType": "geometry.stroke",
140 | "stylers": [
141 | {
142 | "color": "#212a37"
143 | }
144 | ]
145 | },
146 | {
147 | "featureType": "transit",
148 | "elementType": "geometry",
149 | "stylers": [
150 | {
151 | "color": "#2f3948"
152 | }
153 | ]
154 | },
155 | {
156 | "featureType": "transit.station",
157 | "elementType": "labels.text.fill",
158 | "stylers": [
159 | {
160 | "color": "#d59563"
161 | }
162 | ]
163 | },
164 | {
165 | "featureType": "water",
166 | "elementType": "geometry",
167 | "stylers": [
168 | {
169 | "color": "#17263c"
170 | }
171 | ]
172 | },
173 | {
174 | "featureType": "water",
175 | "elementType": "labels.text.fill",
176 | "stylers": [
177 | {
178 | "color": "#515c6d"
179 | }
180 | ]
181 | },
182 | {
183 | "featureType": "water",
184 | "elementType": "labels.text.stroke",
185 | "stylers": [
186 | {
187 | "lightness": -20
188 | }
189 | ]
190 | }
191 | ]
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Android ###
2 | # Built application files
3 | *.apk
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 |
18 | # Gradle files
19 | .gradle/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # IntelliJ
38 | *.iml
39 | .idea/workspace.xml
40 | .idea/tasks.xml
41 | .idea/gradle.xml
42 | .idea/assetWizardSettings.xml
43 | .idea/dictionaries
44 | .idea/libraries
45 | .idea/caches
46 | # Android Studio 3 in .gitignore file.
47 | .idea/caches/build_file_checksums.ser
48 | .idea/modules.xml
49 |
50 | # Keystore files
51 | # Uncomment the following lines if you do not want to check your keystore files in.
52 | #*.jks
53 | #*.keystore
54 |
55 | # External native build folder generated in Android Studio 2.2 and later
56 | .externalNativeBuild
57 |
58 | # Google Services (e.g. APIs or Firebase)
59 | google-services.json
60 |
61 | # Freeline
62 | freeline.py
63 | freeline/
64 | freeline_project_description.json
65 |
66 | # fastlane
67 | fastlane/report.xml
68 | fastlane/Preview.html
69 | fastlane/screenshots
70 | fastlane/test_output
71 | fastlane/readme.md
72 |
73 | # Version control
74 | vcs.xml
75 |
76 | # lint
77 | lint/intermediates/
78 | lint/generated/
79 | lint/outputs/
80 | lint/tmp/
81 | # lint/reports/
82 |
83 | ### Android Patch ###
84 | gen-external-apklibs
85 | output.json
86 |
87 | ### AndroidStudio ###
88 | # Covers files to be ignored for android development using Android Studio.
89 |
90 | # Built application files
91 |
92 | # Files for the ART/Dalvik VM
93 |
94 | # Java class files
95 |
96 | # Generated files
97 |
98 | # Gradle files
99 | .gradle
100 |
101 | # Signing files
102 | .signing/
103 |
104 | # Local configuration file (sdk path, etc)
105 |
106 | # Proguard folder generated by Eclipse
107 |
108 | # Log Files
109 |
110 | # Android Studio
111 | /*/build/
112 | /*/local.properties
113 | /*/out
114 | /*/*/build
115 | /*/*/production
116 | *.ipr
117 | *~
118 | *.swp
119 |
120 | # Android Patch
121 |
122 | # External native build folder generated in Android Studio 2.2 and later
123 |
124 | # NDK
125 | obj/
126 |
127 | # IntelliJ IDEA
128 | *.iws
129 | /out/
130 |
131 | # User-specific configurations
132 | .idea/caches/
133 | .idea/libraries/
134 | .idea/shelf/
135 | .idea/.name
136 | .idea/compiler.xml
137 | .idea/copyright/profiles_settings.xml
138 | .idea/encodings.xml
139 | .idea/misc.xml
140 | .idea/scopes/scope_settings.xml
141 | .idea/vcs.xml
142 | .idea/jsLibraryMappings.xml
143 | .idea/datasources.xml
144 | .idea/dataSources.ids
145 | .idea/sqlDataSources.xml
146 | .idea/dynamic.xml
147 | .idea/uiDesigner.xml
148 |
149 | # OS-specific files
150 | .DS_Store
151 | .DS_Store?
152 | ._*
153 | .Spotlight-V100
154 | .Trashes
155 | ehthumbs.db
156 | Thumbs.db
157 |
158 | # Legacy Eclipse project files
159 | .classpath
160 | .project
161 | .cproject
162 | .settings/
163 |
164 | # Mobile Tools for Java (J2ME)
165 | .mtj.tmp/
166 |
167 | # Package Files #
168 | *.war
169 | *.ear
170 |
171 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
172 | hs_err_pid*
173 |
174 | ## Plugin-specific files:
175 |
176 | # mpeltonen/sbt-idea plugin
177 | .idea_modules/
178 |
179 | # JIRA plugin
180 | atlassian-ide-plugin.xml
181 |
182 | # Mongo Explorer plugin
183 | .idea/mongoSettings.xml
184 |
185 | # Crashlytics plugin (for Android Studio and IntelliJ)
186 | com_crashlytics_export_strings.xml
187 | crashlytics.properties
188 | crashlytics-build.properties
189 | fabric.properties
190 |
191 | ### AndroidStudio Patch ###
192 |
193 | !/gradle/wrapper/gradle-wrapper.jar
194 |
195 | ### Firebase ###
196 | .idea
197 | **/node_modules/*
198 | **/.firebaserc
199 |
200 | ### Firebase Patch ###
201 | .runtimeconfig.json
202 | .firebase/
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_undraw_address.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
24 |
25 |
26 |
27 |
30 |
35 |
36 |
37 |
44 |
45 |
46 |
49 |
52 |
53 |
56 |
59 |
62 |
65 |
68 |
73 |
76 |
79 |
82 |
85 |
86 |
89 |
90 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/vanillaplacepicker/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vanilla Place Picker
2 | [](https://jitpack.io/#Mindinventory/VanillaPlacePicker) 
3 |
4 | Vanilla Place Picker provides a UI that displays an interactive map to get the place details and Autocomplete functionality, which displays place predictions based on user search input.
5 |
6 | Developers often come across a requirement of adding precise location. So, a place picker which is easy to implement, less time consuming, and simple enough for users to use it is always in demand and here we have a Vanilla Place Picker which developer can add it in quick simple steps.
7 |
8 | ## Preview
9 |  
10 |
11 | ## Key features
12 | * Android 13 support
13 | * Simple implementation for place picker either using Autocomplete, Map or both
14 | * Set your own custom map styles
15 | * Customise map pin icon
16 | * Set default location position
17 | * Use it without location permission
18 | * Choose to show only open businesses or all
19 | * Highly customise attributes
20 | * Multi languages support
21 | * RTL layout support
22 |
23 | ## Usage
24 | ### Dependencies
25 |
26 | - **Step 1. Add the JitPack repository in your project build.gradle:**
27 | ```bash
28 | allprojects {
29 | repositories {
30 | ...
31 | maven { url 'https://jitpack.io' }
32 | }
33 | }
34 | ```
35 | **or**
36 | **If Android studio version is Arctic Fox or higher then add it in your settings.gradle**
37 |
38 | ```bash
39 | dependencyResolutionManagement {
40 | repositories {
41 | ...
42 | maven { url 'https://jitpack.io' }
43 | }
44 | }
45 | ```
46 | - **Step 2. Add the dependency in your app module build.gradle file**
47 | ```bash
48 | dependencies {
49 | ...
50 | implementation 'com.github.Mindinventory:VanillaPlacePicker:X.X.X'
51 | }
52 | ```
53 |
54 | ### Implementation
55 | - **Step 1. Add Google MAP API KEY in your local.properties file with the same variable name as defined below (google.maps_api_key)**
56 | ```bash
57 | google.maps_api_key=PLACE YOUR API KEY HERE
58 | ```
59 |
60 | - **Step 2. To get Google MAP Api key from local.properties file, write below defined code in your app module build.gradle file**
61 | ```bash
62 | android {
63 | ...
64 | defaultConfig {
65 | ...
66 | #Access Google MAP Api Key from local.properties file
67 | def properties = new Properties()
68 | file("../local.properties").withInputStream { properties.load(it)
69 | #Share the key with your `AndroidManifest.xml`
70 | manifestPlaceholders = [ googleMapsApiKey:"${properties.getProperty('google.maps_api_key')}"]
71 | }
72 | ```
73 | - **Step 3. Add below defined meta-data code to your AndroidManifest.xml:**
74 |
75 | ```bash
76 |
77 | ...
78 |
79 |
82 |
83 |
84 | ```
85 | - **Step 4. Add VanillaPlacePicker Builder in to your activity class:**
86 | ```bash
87 |
88 | #startActivityForResult is deprecated so better to use registerForActivityResult
89 | var placePickerResultLauncher =
90 | registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
91 | if (result.resultCode == Activity.RESULT_OK && result.data != null) {
92 | val vanillaAddress = VanillaPlacePicker.getPlaceResult(result.data)
93 | }
94 | }
95 |
96 | #Launch caller with Intent
97 | val intent = VanillaPlacePicker.Builder(this)
98 | .with(PickerType.MAP_WITH_AUTO_COMPLETE) // Select Picker type to enable autocompelte, map or both
99 | .withLocation(23.057582, 72.534458)
100 | .setPickerLanguage(PickerLanguage.HINDI) // Apply language to picker
101 | .setLocationRestriction(LatLng(23.0558088,72.5325067), LatLng(23.0587592,72.5357321)) // Restrict location bounds in map and autocomplete
102 | .setCountry("IN") // Only for Autocomplete
103 | .enableShowMapAfterSearchResult(true) // To show the map after selecting the place from place picker only for PickerType.MAP_WITH_AUTO_C
104 | /*
105 | * Configuration for Map UI
106 | */
107 | .setMapType(MapType.SATELLITE) // Choose map type (Only applicable for map screen)
108 | .setMapStyle(R.raw.style_json) // Containing the JSON style declaration for night-mode styling
109 | .setMapPinDrawable(android.R.drawable.ic_menu_mylocation) // To give custom pin image for map marker
110 | .build()
111 |
112 | placePickerResultLauncher.launch(intent)
113 |
114 | ```
115 |
116 | ## Requirements
117 |
118 | * minSdkVersion >= 21
119 | * Androidx
120 |
121 | # LICENSE!
122 |
123 | Vanilla Place Picker is [MIT-licensed](/LICENSE).
124 |
125 | # Let us know!
126 | We’d be really happy if you send us links to your projects where you use our component. Just send an email to sales@mindinventory.com And do let us know if you have any questions or suggestion regarding our work.
127 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/builder/VanillaPlacePicker.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.builder
2 |
3 | import android.content.Context
4 | import android.content.ContextWrapper
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.util.Log
8 | import com.google.android.gms.maps.model.LatLng
9 | import com.google.android.libraries.places.widget.Autocomplete
10 | import com.vanillaplacepicker.data.VanillaAddress
11 | import com.vanillaplacepicker.data.common.AutoCompleteAddressDetailsMapper
12 | import com.vanillaplacepicker.extenstion.isRequiredField
13 | import com.vanillaplacepicker.presentation.map.VanillaMapActivity
14 | import com.vanillaplacepicker.utils.*
15 | import wrap
16 |
17 |
18 | class VanillaPlacePicker {
19 |
20 | companion object {
21 | private val TAG = VanillaPlacePicker::class.java.simpleName
22 |
23 | fun getPlaceResult(data: Intent?): VanillaAddress? {
24 | if (data != null) {
25 | val selectedPlace = data.getSerializableExtra(KeyUtils.SELECTED_PLACE)
26 | return if (selectedPlace is VanillaAddress) {
27 | selectedPlace
28 | } else {
29 | val place = Autocomplete.getPlaceFromIntent(data)
30 | AutoCompleteAddressDetailsMapper.apply(place)
31 | }
32 | }
33 | return null
34 | }
35 | }
36 |
37 | class Builder(private val context: Context) {
38 | private val vanillaConfig = VanillaConfig()
39 |
40 | /**
41 | * To enable map view with place picker
42 | */
43 | fun with(pickerType: PickerType): Builder {
44 | vanillaConfig.pickerType = pickerType
45 | return this
46 | }
47 |
48 | /**
49 | * Filter addresses by country name ex. "US"
50 | */
51 | fun setCountry(country: String): Builder {
52 | vanillaConfig.country = country
53 | return this
54 | }
55 |
56 | /**
57 | * Request with default latitude & longitude for near by places
58 | */
59 | fun withLocation(latitude: Double, longitude: Double): Builder {
60 | vanillaConfig.latitude = latitude
61 | vanillaConfig.longitude = longitude
62 | return this
63 | }
64 |
65 | /**
66 | * To restrict request zone by rect
67 | */
68 | fun setLocationRestriction(leftLatLng: LatLng, rightLatLng: LatLng): Builder {
69 | vanillaConfig.zoneRect = SearchZoneRect(leftLatLng, rightLatLng)
70 | return this
71 | }
72 |
73 | /**
74 | * Add a map with custom styling
75 | * With style options you can customize the presentation of the standard Google map styles,
76 | * changing the visual display of features like roads, parks, businesses, and other points of interest.
77 | * Add a resource containing a JSON style object (Use a raw resource)
78 | * */
79 | fun setMapStyle(jsonResourceIdMapStyle: Int): Builder {
80 | vanillaConfig.mapStyleJSONResId = jsonResourceIdMapStyle
81 | return this
82 | }
83 |
84 | /**
85 | * Add a map with custom styling
86 | * With style options you can customize the presentation of the standard Google map styles,
87 | * changing the visual display of features like roads, parks, businesses, and other points of interest.
88 | * Add a resource containing a JSON style object (Use a string resource)
89 | * */
90 | fun setMapType(mapType: MapType): Builder {
91 | vanillaConfig.mapType = mapType
92 | return this
93 | }
94 |
95 | /**
96 | * To show the map after selecting the place from place picker
97 | * To insure that user needs to navigate to map screen after the selecting the place from the place picker
98 | * @param enableShowMapAfterSearchResult true to show the map after search result, false otherwise
99 | * @return Returns a VanillaPlacePicker.Builder instance.
100 | */
101 | fun enableShowMapAfterSearchResult(enableShowMapAfterSearchResult: Boolean): Builder {
102 | vanillaConfig.enableShowMapAfterSearchResult = enableShowMapAfterSearchResult
103 | return this
104 | }
105 |
106 | /**
107 | * Set custom Map Pin image
108 | * */
109 | fun setMapPinDrawable(mapPinDrawableResId: Int): Builder {
110 | vanillaConfig.mapPinDrawable = mapPinDrawableResId
111 | return this
112 | }
113 |
114 | /**
115 | * Set picker language
116 | */
117 | fun setPickerLanguage(pickerLanguage: PickerLanguage): Builder {
118 | ContextWrapper(context).wrap(pickerLanguage.value)
119 | return this
120 | }
121 |
122 | /**
123 | * Get Google Places API key
124 | */
125 | private fun getApiKey(): String? {
126 | val metadataBundle: Bundle = BundleUtils.getMetaData(context)
127 | return if (metadataBundle.getString("com.google.android.geo.API_KEY").isRequiredField())
128 | metadataBundle.getString("com.google.android.geo.API_KEY")
129 | else {
130 | Log.e(
131 | TAG,
132 | "Couldn't get Google API key from application meta data. Was it set in your AndroidManifest.xml?"
133 | )
134 | ""
135 | }
136 | }
137 |
138 | fun build(): Intent {
139 | vanillaConfig.apiKey = getApiKey()
140 | val intent = if (vanillaConfig.pickerType == PickerType.AUTO_COMPLETE) {
141 | AutoCompleteUtils.getAutoCompleteIntent(context, vanillaConfig)
142 | } else {
143 | Intent(context, VanillaMapActivity::class.java)
144 | }
145 | intent.putExtra(KeyUtils.EXTRA_CONFIG, vanillaConfig)
146 | return intent
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
20 |
21 |
22 |
29 |
30 |
37 |
38 |
45 |
46 |
54 |
55 |
56 |
57 |
58 |
65 |
66 |
73 |
74 |
81 |
82 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
103 |
104 |
111 |
112 |
119 |
120 |
129 |
130 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/service/FetchAddressIntentService.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.service
2 |
3 | import android.app.IntentService
4 | import android.content.Intent
5 | import android.location.Address
6 | import android.location.Geocoder
7 | import android.os.Bundle
8 | import android.os.ResultReceiver
9 | import android.util.Log
10 | import com.google.android.gms.maps.model.LatLng
11 | import com.google.gson.Gson
12 | import com.vanillaplacepicker.R
13 | import com.vanillaplacepicker.data.GeoCoderAddressResponse
14 | import com.vanillaplacepicker.utils.KeyUtils
15 | import com.vanillaplacepicker.utils.Logger
16 | import java.io.IOException
17 | import java.util.*
18 |
19 | class FetchAddressIntentService : IntentService("FetchAddressIntentService") {
20 | private val TAG = FetchAddressIntentService::class.java.simpleName
21 | /**
22 | * The receiver where results are forwarded from this service.
23 | */
24 | private var receiver: ResultReceiver? = null
25 |
26 | /**
27 | * Tries to get the location address using a Geocoder. If successful, sends an address to a
28 | * result receiver. If unsuccessful, sends an error message instead.
29 | * Note: We define a [android.os.ResultReceiver] in * VanillaMapActivity to process content
30 | * sent from this service.
31 | *
32 | * This service calls this method from the default worker thread with the intent that started
33 | * the service. When this method returns, the service automatically stops.
34 | */
35 | override fun onHandleIntent(intent: Intent?) {
36 | var errorMessage = ""
37 |
38 | receiver = intent?.getParcelableExtra(KeyUtils.RECEIVER)
39 |
40 | // Check if receiver was properly registered.
41 | if (intent == null || receiver == null) {
42 | Log.wtf(TAG, "No receiver received. There is nowhere to send the results.")
43 | return
44 | }
45 |
46 | // Get the location passed to this service through an extra.
47 | val latlng = intent.getParcelableExtra(KeyUtils.LOCATION_DATA_EXTRA)
48 |
49 | // Make sure that the location data was really sent over through an extra. If it wasn't,
50 | // send an error error message and return.
51 | if (latlng == null) {
52 | errorMessage = getString(R.string.no_location_data_provided)
53 | Log.wtf(TAG, errorMessage)
54 | deliverResultToReceiver(KeyUtils.FAILURE_RESULT, errorMessage, null)
55 | return
56 | }
57 |
58 | // Errors could still arise from using the Geocoder (for example, if there is no
59 | // connectivity, or if the Geocoder is given illegal location data). Or, the Geocoder may
60 | // simply not have an address for a location. In all these cases, we communicate with the
61 | // receiver using a resultCode indicating failure. If an address is found, we use a
62 | // resultCode indicating success.
63 |
64 | // The Geocoder used in this sample. The Geocoder's responses are localized for the given
65 | // Locale, which represents a specific geographical or linguistic region. Locales are used
66 | // to alter the presentation of information such as numbers or dates to suit the conventions
67 | // in the region they describe.
68 | val geocoder = Geocoder(this, Locale.getDefault())
69 |
70 | // Address found using the Geocoder.
71 | var addresses: List = emptyList()
72 |
73 | try {
74 | // Using getFromLocation() returns an array of Addresses for the area immediately
75 | // surrounding the given latitude and longitude. The results are a best guess and are
76 | // not guaranteed to be accurate.
77 | addresses = geocoder.getFromLocation(
78 | latlng.latitude,
79 | latlng.longitude,
80 | // In this sample, we get just a single address.
81 | 1
82 | ) as List
83 | } catch (ioException: IOException) {
84 | // Catch network or other I/O problems.
85 | Logger.e(TAG, ioException.toString())
86 | deliverResultToReceiver(KeyUtils.FAILURE_RESULT, getString(R.string.service_not_available), null)
87 | } catch (illegalArgumentException: IllegalArgumentException) {
88 | // Catch invalid latitude or longitude values.
89 | Logger.e(
90 | TAG, "Latitude = $latlng.latitude , " +
91 | "Longitude = $latlng.longitude $illegalArgumentException"
92 | )
93 | deliverResultToReceiver(KeyUtils.FAILURE_RESULT, getString(R.string.invalid_lat_long_used), null)
94 | }
95 |
96 | // Handle case where no address was found.
97 | if (addresses.isEmpty()) {
98 | if (errorMessage.isEmpty()) {
99 | errorMessage = getString(R.string.no_address_found)
100 | Logger.e(TAG, errorMessage)
101 | }
102 | deliverResultToReceiver(KeyUtils.FAILURE_RESULT, errorMessage, null)
103 | } else {
104 | Logger.d(TAG, "Addresses >> ${Gson().toJson(addresses)}")
105 | val address = addresses[0]
106 | // Fetch the address lines using {@code getAddressLine},
107 | // join them, and send them to the thread. The {@link android.location.address}
108 | // class provides other options for fetching address details that you may prefer
109 | // to use. Here are some examples:
110 | // getLocality() ("Mountain View", for example)
111 | // getAdminArea() ("CA", for example)
112 | // getPostalCode() ("94043", for example)
113 | // getCountryCode() ("US", for example)
114 | // getCountryName() ("United States", for example)
115 | val addressFragments = with(address) {
116 | (0..maxAddressLineIndex).map { getAddressLine(it) }
117 | }
118 |
119 | val selectedPlace = GeoCoderAddressResponse(
120 | addressFragments.joinToString(separator = "\n"),
121 | address.featureName,
122 | address.adminArea,
123 | address.subAdminArea,
124 | address.locality,
125 | address.subThoroughfare,
126 | address.postalCode,
127 | address.countryCode,
128 | address.countryName,
129 | address.hasLatitude(),
130 | address.latitude,
131 | address.hasLongitude(),
132 | address.longitude,
133 | address.phone,
134 | address.url,
135 | address.extras
136 | )
137 |
138 | Logger.i(TAG, getString(R.string.address_found))
139 | deliverResultToReceiver(
140 | KeyUtils.SUCCESS_RESULT,
141 | getString(R.string.address_found),
142 | selectedPlace
143 | )
144 | }
145 | }
146 |
147 | /**
148 | * Sends a resultCode and message to the receiver.
149 | */
150 | private fun deliverResultToReceiver(resultCode: Int, message: String, selectedPlace: GeoCoderAddressResponse?) {
151 | val bundle = Bundle().apply {
152 | putString(KeyUtils.RESULT_MESSAGE_KEY, message)
153 | putSerializable(KeyUtils.RESULT_DATA_KEY, selectedPlace)
154 | }
155 | receiver?.send(resultCode, bundle)
156 | }
157 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_undraw_map.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
14 |
17 |
20 |
23 |
26 |
29 |
32 |
35 |
40 |
43 |
46 |
51 |
58 |
65 |
72 |
79 |
86 |
93 |
100 |
107 |
114 |
121 |
128 |
135 |
142 |
145 |
148 |
153 |
156 |
161 |
164 |
167 |
170 |
173 |
176 |
179 |
182 |
185 |
190 |
195 |
198 |
201 |
204 |
207 |
210 |
213 |
216 |
219 |
224 |
225 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_undraw_note_list.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
17 |
22 |
27 |
32 |
37 |
42 |
47 |
52 |
57 |
62 |
67 |
72 |
77 |
82 |
85 |
88 |
91 |
94 |
97 |
100 |
103 |
106 |
109 |
112 |
115 |
118 |
121 |
124 |
127 |
130 |
135 |
140 |
143 |
148 |
153 |
156 |
161 |
166 |
169 |
175 |
181 |
187 |
193 |
198 |
203 |
209 |
215 |
220 |
223 |
226 |
229 |
232 |
237 |
242 |
247 |
252 |
255 |
260 |
263 |
266 |
269 |
272 |
277 |
282 |
285 |
288 |
293 |
296 |
299 |
302 |
305 |
308 |
311 |
314 |
317 |
320 |
323 |
328 |
333 |
334 |
--------------------------------------------------------------------------------
/vanillaplacepicker/src/main/java/com/vanillaplacepicker/presentation/map/VanillaMapActivity.kt:
--------------------------------------------------------------------------------
1 | package com.vanillaplacepicker.presentation.map
2 |
3 | import android.Manifest
4 | import android.app.Activity
5 | import android.content.Intent
6 | import android.content.IntentSender
7 | import android.content.pm.PackageManager
8 | import android.os.*
9 | import android.util.Log
10 | import android.view.LayoutInflater
11 | import android.view.View
12 | import android.widget.RelativeLayout
13 | import androidx.activity.result.IntentSenderRequest
14 | import androidx.activity.result.contract.ActivityResultContracts
15 | import androidx.core.app.ActivityCompat
16 | import androidx.core.content.ContextCompat
17 | import androidx.lifecycle.ViewModelProvider
18 | import com.google.android.gms.common.api.ApiException
19 | import com.google.android.gms.common.api.ResolvableApiException
20 | import com.google.android.gms.location.*
21 | import com.google.android.gms.maps.CameraUpdateFactory
22 | import com.google.android.gms.maps.GoogleMap
23 | import com.google.android.gms.maps.OnMapReadyCallback
24 | import com.google.android.gms.maps.SupportMapFragment
25 | import com.google.android.gms.maps.model.LatLng
26 | import com.google.android.gms.maps.model.LatLngBounds
27 | import com.google.android.gms.maps.model.MapStyleOptions
28 | import com.vanillaplacepicker.R
29 | import com.vanillaplacepicker.data.GeoCoderAddressResponse
30 | import com.vanillaplacepicker.data.common.AddressMapperGoogleMap
31 | import com.vanillaplacepicker.databinding.ActivityVanillaMapBinding
32 | import com.vanillaplacepicker.domain.common.SafeObserver
33 | import com.vanillaplacepicker.extenstion.*
34 | import com.vanillaplacepicker.presentation.builder.VanillaConfig
35 | import com.vanillaplacepicker.presentation.builder.VanillaPlacePicker
36 | import com.vanillaplacepicker.presentation.common.VanillaBaseViewModelActivity
37 | import com.vanillaplacepicker.service.FetchAddressIntentService
38 | import com.vanillaplacepicker.utils.*
39 |
40 | class VanillaMapActivity :
41 | VanillaBaseViewModelActivity(),
42 | OnMapReadyCallback,
43 | View.OnClickListener {
44 | companion object {
45 | private val TAG = this::class.java.simpleName
46 | }
47 |
48 | private var mapFragment: SupportMapFragment? = null
49 | private var googleMap: GoogleMap? = null
50 | private lateinit var resultReceiver: AddressResultReceiver
51 | private var selectedPlace: GeoCoderAddressResponse? = null
52 | private var midLatLng: LatLng? = null
53 | private var defaultZoomLoaded = false
54 | private var fusedLocationProviderClient: FusedLocationProviderClient? = null
55 | private val startLocationHandler = Handler(Looper.getMainLooper())
56 |
57 | private var vanillaConfig: VanillaConfig? = null
58 |
59 | // Belows are used in PlacePickerActivity
60 | private var isRequestedWithLocation = false
61 | private val sharedPrefs by lazy { SharedPrefs(this) }
62 | private var fetchLocationForFirstTime = false
63 |
64 | private var placePickerResultLauncher =
65 | registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
66 | when (result.resultCode) {
67 | Activity.RESULT_OK -> {
68 | // data contains Place object
69 | if (vanillaConfig?.enableShowMapAfterSearchResult == true) {
70 | navigateMapToResult(result.data)
71 | } else {
72 | setResult(Activity.RESULT_OK, result.data)
73 | finish()
74 | }
75 | }
76 | }
77 | }
78 |
79 | override fun inflateLayout(layoutInflater: LayoutInflater) =
80 | ActivityVanillaMapBinding.inflate(layoutInflater)
81 |
82 | override fun buildViewModel(): VanillaMapViewModel {
83 | return ViewModelProvider(this, VanillaMapViewModelFactory(sharedPrefs)).get(
84 | VanillaMapViewModel::class.java
85 | )
86 | }
87 |
88 | private val launcher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
89 | when (result.resultCode) {
90 | Activity.RESULT_CANCELED -> viewModel.fetchSavedLocation()
91 | Activity.RESULT_OK -> postDelayed()
92 | }
93 | }
94 |
95 | override fun initViews() {
96 | super.initViews()
97 | // HIDE ActionBar(if exist in style) of root project module
98 | supportActionBar?.hide()
99 | setMapPinDrawable()
100 | with(binding.customToolbar) {
101 | tvAddress.isSelected = true
102 | ivBack.setOnClickListener(this@VanillaMapActivity)
103 | ivDone.setOnClickListener(this@VanillaMapActivity)
104 | if (vanillaConfig?.pickerType == PickerType.MAP_WITH_AUTO_COMPLETE) {
105 | tvAddress.setOnClickListener(this@VanillaMapActivity)
106 | }
107 | }
108 | binding.fabLocation.setOnClickListener(this)
109 |
110 | fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
111 |
112 | if (!isRequestedWithLocation) {
113 | startLocationUpdates()
114 | }
115 | mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
116 | mapFragment?.getMapAsync(this)
117 | resultReceiver = AddressResultReceiver(Handler(Looper.getMainLooper()))
118 | }
119 |
120 | override fun getBundle() {
121 | if (hasExtra(KeyUtils.EXTRA_CONFIG)) {
122 | vanillaConfig = intent.getParcelableExtra(KeyUtils.EXTRA_CONFIG)
123 | }
124 | if (vanillaConfig?.latitude != KeyUtils.DEFAULT_LOCATION && vanillaConfig?.longitude != KeyUtils.DEFAULT_LOCATION) {
125 | isRequestedWithLocation = true
126 | }
127 | startLocationRequestPermission()
128 | }
129 |
130 | // Set custom image drawable to Map Pin
131 | private fun setMapPinDrawable() {
132 | try {
133 | vanillaConfig?.mapPinDrawable?.let { pinDrawableResId ->
134 | binding.ivMarker.setImageDrawable(ContextCompat.getDrawable(this, pinDrawableResId))
135 | }
136 | } catch (e: Exception) {
137 | Logger.e(TAG, "Invalid drawable resource ID. Error: $e")
138 | }
139 | }
140 |
141 | /**
142 | * this runnable will help to provide adequate delay before fetching device location.
143 | */
144 | private val startLocationRunnable = Runnable {
145 | getLocationFromFusedLocation()
146 | }
147 |
148 | private fun removeCallbacks() {
149 | startLocationHandler.removeCallbacks(startLocationRunnable)
150 | }
151 |
152 | private fun postDelayed() {
153 | startLocationHandler.postDelayed(startLocationRunnable, KeyUtils.LOCATION_UPDATE_INTERVAL)
154 | }
155 |
156 | override fun onClick(v: View?) {
157 | when (v?.id) {
158 | R.id.ivBack -> onBackPressed()
159 | R.id.ivDone -> {
160 | selectedPlace ?: return
161 | setResult(Activity.RESULT_OK, Intent().apply {
162 | putExtra(
163 | KeyUtils.SELECTED_PLACE,
164 | selectedPlace?.let { AddressMapperGoogleMap.apply(it) })
165 | })
166 | finish()
167 | }
168 | R.id.tvAddress ->
169 | placePickerResultLauncher.launch(vanillaConfig?.let {
170 | AutoCompleteUtils.getAutoCompleteIntent(
171 | this,
172 | it
173 | )
174 | })
175 | R.id.fabLocation -> isGpsEnabled()
176 | }
177 | }
178 |
179 | /**
180 | * Receiver for data sent from FetchAddressIntentService.
181 | */
182 | private inner class AddressResultReceiver(
183 | handler: Handler,
184 | ) : ResultReceiver(handler) {
185 | /**
186 | * Receives data sent from FetchAddressIntentService and updates the UI.
187 | */
188 | override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
189 | if (!resultData.containsKey(KeyUtils.RESULT_DATA_KEY)) {
190 | return
191 | }
192 | when (resultCode) {
193 | KeyUtils.SUCCESS_RESULT -> {
194 | selectedPlace =
195 | resultData.getSerializable(KeyUtils.RESULT_DATA_KEY) as GeoCoderAddressResponse
196 | with(binding.customToolbar) {
197 | if (selectedPlace?.addressLine.isRequiredField()) {
198 | Logger.d(
199 | TAG,
200 | "AddressResultReceiver.onReceiveResult: address: ${selectedPlace?.addressLine}"
201 | )
202 | ivDone.show()
203 | tvAddress.text = selectedPlace?.addressLine
204 | } else {
205 | ivDone.hide()
206 | }
207 | }
208 | }
209 | KeyUtils.FAILURE_RESULT -> {
210 | val errorMessage = resultData.getString(KeyUtils.RESULT_MESSAGE_KEY)
211 | ToastUtils.showToast(this@VanillaMapActivity, errorMessage)
212 | }
213 | else -> {
214 | // make address empty
215 | }
216 | }
217 | }
218 | }
219 |
220 | /**
221 | * Manipulates the map when it's available.
222 | * The API invokes this callback when the map is ready for use.
223 | */
224 | override fun onMapReady(googleMap: GoogleMap) {
225 | this.googleMap = googleMap
226 | this.googleMap?.clear()
227 | if (vanillaConfig?.enableSatelliteView == true)
228 | this.googleMap?.mapType = GoogleMap.MAP_TYPE_SATELLITE
229 |
230 | // Customise the styling of the base map using a JSON object defined...
231 | try {
232 | // ...in a raw resource file.
233 | if (vanillaConfig?.mapStyleJSONResId != KeyUtils.DEFAULT_STYLE_JSON_RESID) {
234 | this.googleMap?.setMapStyle(
235 | vanillaConfig?.let {
236 | MapStyleOptions.loadRawResourceStyle(
237 | this@VanillaMapActivity,
238 | it.mapStyleJSONResId
239 | )
240 | }
241 | )
242 | }
243 | // ...in a string resource file.
244 | this.googleMap?.mapType = vanillaConfig.let { it?.mapType?.value!! }
245 | } catch (e: Exception) {
246 | Logger.e(TAG, "Can't find map style or Style parsing failed. Error: $e")
247 | }
248 | val cameraUpdateDefaultLocation =
249 | vanillaConfig?.let { vanillaConfig?.latitude?.let { it1 -> LatLng(it1, it.longitude) } }
250 | ?.let {
251 | CameraUpdateFactory.newLatLngZoom(
252 | it,
253 | if (vanillaConfig?.latitude == KeyUtils.DEFAULT_LOCATION) 0f else KeyUtils.DEFAULT_ZOOM_LEVEL
254 | )
255 | }
256 | if (cameraUpdateDefaultLocation != null) {
257 | this.googleMap?.animateCamera(
258 | cameraUpdateDefaultLocation,
259 | KeyUtils.GOOGLE_MAP_CAMERA_ANIMATE_DURATION,
260 | null
261 | )
262 | }
263 | /**
264 | * Set Padding: Top to show CompassButton at visible position on map
265 | * (Before allow Location runtime permission, After that changed position of CompassButton)
266 | * */
267 | this.googleMap?.setPadding(0, 256, 0, 0)
268 | vanillaConfig?.zoneRect?.let {
269 | this.googleMap?.setLatLngBoundsForCameraTarget(
270 | LatLngBounds(
271 | it.lowerLeft,
272 | it.upperRight
273 | )
274 | )
275 | }
276 |
277 | this.googleMap?.setOnCameraMoveListener {
278 | with(binding.customToolbar)
279 | {
280 | tvAddress.text = getString(R.string.searching)
281 | ivDone.hide()
282 | }
283 | }
284 | this.googleMap?.setOnCameraIdleListener {
285 | val newLatLng = this@VanillaMapActivity.googleMap?.cameraPosition?.target
286 | newLatLng?.let {
287 | midLatLng?.let { midLatLng ->
288 | if (it.latitude == midLatLng.latitude && it.longitude == midLatLng.longitude) {
289 | return@setOnCameraIdleListener
290 | }
291 | }
292 | midLatLng = this@VanillaMapActivity.googleMap?.cameraPosition?.target
293 | midLatLng?.let { centerPoint ->
294 | startReverseGeoCodingService(centerPoint)
295 | }
296 | }
297 | }
298 | }
299 |
300 | override fun initLiveDataObservers() {
301 | super.initLiveDataObservers()
302 | viewModel.latLngLiveData.observe(this, SafeObserver(this::moveCameraToLocation))
303 | }
304 |
305 | private fun moveCameraToLocation(latLng: LatLng?) {
306 | latLng ?: return
307 |
308 | val zoomLevel = if (defaultZoomLoaded) {
309 | defaultZoomLoaded = false
310 | googleMap?.cameraPosition?.zoom ?: KeyUtils.DEFAULT_ZOOM_LEVEL
311 | } else {
312 | KeyUtils.DEFAULT_ZOOM_LEVEL
313 | }
314 | val location = CameraUpdateFactory.newLatLngZoom(latLng, zoomLevel)
315 | googleMap?.animateCamera(location)
316 |
317 | changeLocationCompassButtonPosition()
318 | if (ActivityCompat.checkSelfPermission(
319 | this,
320 | Manifest.permission.ACCESS_FINE_LOCATION
321 | ) == PackageManager.PERMISSION_GRANTED
322 | ) {
323 | this.googleMap?.isMyLocationEnabled = true
324 | this.googleMap?.uiSettings?.isMyLocationButtonEnabled = false
325 | changeMyLocationButtonPosition()
326 | }
327 | }
328 |
329 | /*
330 | * Change location compass button position
331 | * From: Default
332 | * To: Top - Right
333 | * */
334 | private fun changeLocationCompassButtonPosition() {
335 | try {
336 | val locationCompassButton =
337 | (mapFragment?.view?.findViewById(Integer.parseInt("1"))?.parent as View)
338 | .findViewById(Integer.parseInt("5"))
339 | val rlp = locationCompassButton.layoutParams as RelativeLayout.LayoutParams
340 | rlp.addRule(RelativeLayout.ALIGN_PARENT_START, 0)
341 | rlp.addRule(RelativeLayout.ALIGN_PARENT_END, RelativeLayout.TRUE)
342 | rlp.marginEnd = 16
343 | } catch (ex: Exception) {
344 | ex.printStackTrace()
345 | }
346 | }
347 |
348 | /*
349 | * Change my location button position
350 | * From: Default
351 | * To: Bottom - Right
352 | * */
353 | private fun changeMyLocationButtonPosition() {
354 | val locationButton =
355 | (mapFragment?.view?.findViewById(Integer.parseInt("1"))?.parent as View)
356 | .findViewById(Integer.parseInt("2"))
357 | val rlp = locationButton.layoutParams as RelativeLayout.LayoutParams
358 | rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP, 0)
359 | rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE)
360 | rlp.setMargins(0, 0, 0, 32)
361 | }
362 |
363 | /**
364 | * Creates an intent, adds location data to it as an extra, and starts the intent service for
365 | * fetching an address.
366 | */
367 | private fun startReverseGeoCodingService(latLng: LatLng) {
368 | val lat = latLng.latitude
369 | val lng = latLng.longitude
370 |
371 | if (lat != KeyUtils.DEFAULT_LOCATION && lng != KeyUtils.DEFAULT_LOCATION) {
372 | // Create an intent for passing to the intent service responsible for fetching the address.
373 | val intent = Intent(this, FetchAddressIntentService::class.java).apply {
374 | // Pass the result receiver as an extra to the service.
375 | putExtra(KeyUtils.RECEIVER, resultReceiver)
376 | // Pass the location data as an extra to the service.
377 | putExtra(KeyUtils.LOCATION_DATA_EXTRA, latLng)
378 | }
379 | // Start the service. If the service isn't already running, it is instantiated and started
380 | // (creating a process for it if needed); if it is running then it remains running. The
381 | // service kills itself automatically once all intents are processed.
382 | startService(intent)
383 | }
384 | }
385 |
386 | private fun navigateMapToResult(data: Intent?) {
387 | val vanillaAddress = VanillaPlacePicker.getPlaceResult(data)
388 | this.googleMap?.animateCamera(
389 | CameraUpdateFactory.newLatLngZoom(
390 | LatLng(
391 | vanillaAddress?.latitude ?: KeyUtils.DEFAULT_LOCATION,
392 | vanillaAddress?.longitude ?: KeyUtils.DEFAULT_LOCATION
393 | ),
394 | KeyUtils.DEFAULT_ZOOM_LEVEL
395 | ),
396 | KeyUtils.GOOGLE_MAP_CAMERA_ANIMATE_DURATION,
397 | null
398 | )
399 | }
400 |
401 | override fun onRequestPermissionsResult(
402 | requestCode: Int,
403 | permissions: Array,
404 | grantResults: IntArray,
405 | ) {
406 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
407 | when (requestCode) {
408 | KeyUtils.REQUEST_PERMISSIONS_REQUEST_CODE -> {
409 | when {
410 | grantResults.isEmpty() -> {
411 | // If user interaction was interrupted, the permission request is cancelled and you receive empty arrays.
412 | Log.d(TAG, resources.getString(R.string.user_interaction_was_cancelled))
413 | }
414 | grantResults[0] == PackageManager.PERMISSION_GRANTED -> {
415 | if (!isRequestedWithLocation) {
416 | startLocationUpdates()
417 | }
418 |
419 | }
420 | else -> showAlertDialog(
421 | R.string.missing_permission_message,
422 | R.string.missing_permission_title,
423 | R.string.permission,
424 | R.string.cancel, {
425 | // this mean user has clicked on permission button to update run time permission.
426 | openAppSetting()
427 | }
428 | )
429 | }
430 | }
431 | }
432 | }
433 |
434 | private fun startLocationRequestPermission() {
435 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
436 | // this mean device os is greater or equal to Marshmallow.
437 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
438 | != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
439 | this,
440 | Manifest.permission.ACCESS_COARSE_LOCATION
441 | ) != PackageManager.PERMISSION_GRANTED
442 | ) {
443 | // here we are going to request location run time permission.
444 | ActivityCompat.requestPermissions(
445 | this,
446 | arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
447 | KeyUtils.REQUEST_PERMISSIONS_REQUEST_CODE
448 | )
449 | return
450 | }
451 | }
452 | }
453 |
454 | private val locationRequest = LocationRequest.create().apply {
455 | this.priority = Priority.PRIORITY_HIGH_ACCURACY
456 | this.interval = KeyUtils.DEFAULT_FETCH_LOCATION_INTERVAL
457 | }
458 |
459 | private val locationSettingRequest = LocationSettingsRequest.Builder()
460 | .addLocationRequest(locationRequest)
461 |
462 | /**
463 | * this method will check required for location and according to result it will go ahead for fetching location.
464 | */
465 | private fun startLocationUpdates() {
466 | // Begin by checking if the device has the necessary location settings.
467 | LocationServices.getSettingsClient(this)
468 | .checkLocationSettings(locationSettingRequest.build())
469 | .addOnSuccessListener(this) {
470 | getLocationFromFusedLocation()
471 | }.addOnFailureListener(this) { e ->
472 | when ((e as ApiException).statusCode) {
473 | LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
474 | Logger.i(
475 | TAG,
476 | resources.getString(R.string.location_settings_are_not_satisfied)
477 | )
478 | try {
479 | val rae = e as ResolvableApiException
480 | launcher.launch(IntentSenderRequest.Builder(rae.resolution).build())
481 | } catch (sie: IntentSender.SendIntentException) {
482 | Logger.i(
483 | TAG,
484 | getString(R.string.pendingintent_unable_to_execute_request)
485 | )
486 | viewModel.fetchSavedLocation()
487 | sie.printStackTrace()
488 | }
489 | }
490 | LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
491 | val errorMessage =
492 | resources.getString(R.string.location_settings_are_inadequate_and_cannot_be_fixed_here)
493 | Logger.e(TAG, errorMessage)
494 | viewModel.fetchSavedLocation()
495 | }
496 | }
497 | }
498 | }
499 |
500 | private fun getLocationFromFusedLocation() {
501 | if (ActivityCompat.checkSelfPermission(
502 | this,
503 | Manifest.permission.ACCESS_FINE_LOCATION
504 | ) != PackageManager.PERMISSION_GRANTED
505 | && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
506 | != PackageManager.PERMISSION_GRANTED
507 | ) {
508 | return
509 | }
510 |
511 | fusedLocationProviderClient?.flushLocations()
512 | Looper.myLooper()?.let {
513 | fusedLocationProviderClient?.requestLocationUpdates(
514 | locationRequest,
515 | object : LocationCallback() {
516 | override fun onLocationResult(locationResult: LocationResult) {
517 | super.onLocationResult(locationResult)
518 | val location = locationResult.lastLocation
519 | Log.e("location", location.toString())
520 | if (location != null) {
521 | viewModel.saveLatLngToSharedPref(location.latitude, location.longitude)
522 | }
523 | if (!fetchLocationForFirstTime) {
524 | viewModel.fetchSavedLocation()
525 | fetchLocationForFirstTime = true
526 | }
527 | }
528 |
529 | override fun onLocationAvailability(locationAvailability: LocationAvailability) {
530 | super.onLocationAvailability(locationAvailability)
531 | if (!locationAvailability.isLocationAvailable) {
532 | viewModel.fetchSavedLocation()
533 | }
534 | }
535 | },
536 | it
537 | )
538 | }
539 | }
540 |
541 |
542 | private fun stopLocationUpdates() {
543 | // here, we are removing callback from runnable for handler so it will get called ahead.
544 | removeCallbacks()
545 | }
546 |
547 | override fun onDestroy() {
548 | super.onDestroy()
549 | stopLocationUpdates()
550 | }
551 |
552 | private fun isGpsEnabled() {
553 | if (!fetchLocationForFirstTime) {
554 | startLocationUpdates()
555 | } else {
556 | viewModel.fetchSavedLocation()
557 | }
558 | }
559 | }
560 |
--------------------------------------------------------------------------------