diff --git a/README.md b/README.md
index b760dc8..3b08923 100644
--- a/README.md
+++ b/README.md
@@ -3,15 +3,21 @@
## Description
It is a sample app with basic google maps functionality.
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
## About MLSDev
diff --git a/app/build.gradle b/app/build.gradle
index b317322..7f9f1b1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,13 +1,12 @@
apply plugin: 'com.android.application'
-apply plugin: 'me.tatarka.retrolambda'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-android'
android {
- compileSdkVersion 25
- buildToolsVersion "25.0.0"
+ compileSdkVersion 28
defaultConfig {
applicationId "com.mlsdev.mapsappsample"
- minSdkVersion 18
- targetSdkVersion 25
+ minSdkVersion 19
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -19,50 +18,54 @@ android {
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
dataBinding {
enabled = true
}
- compileOptions {
- targetCompatibility 1.8
- sourceCompatibility 1.8
- }
}
ext {
- googleServicesVersion = '9.6.0'
- supportVersion = '25.0.0'
retrofitVersion = '2.1.0'
powermockVersion = '1.6.5'
dexmarkVersion = '1.2'
junitVersion = '4.12'
- supportTestsLibVersion = '0.5'
- espressoVersion = '2.2.2'
okhttpVersion = '3.4.1'
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile "com.android.support:appcompat-v7:$supportVersion"
- testCompile "junit:junit:$junitVersion"
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ testImplementation "junit:junit:$junitVersion"
+
+ def androidx_version = '1.0.0'
+ def androidx_appcompat_version = '1.0.2'
+ implementation "androidx.appcompat:appcompat:$androidx_appcompat_version"
+ implementation "androidx.cardview:cardview:$androidx_version"
+ implementation "androidx.recyclerview:recyclerview:$androidx_version"
+ implementation "com.google.android.material:material:$androidx_version"
// Maps, Places and Location
- compile "com.google.android.gms:play-services-maps:$googleServicesVersion"
- compile "com.google.android.gms:play-services-places:$googleServicesVersion"
- compile "com.google.android.gms:play-services-location:$googleServicesVersion"
+ def googleServicesVersion = '17.0.0'
+ implementation "com.google.android.gms:play-services-maps:$googleServicesVersion"
+ implementation "com.google.android.gms:play-services-places:$googleServicesVersion"
+ implementation "com.google.android.gms:play-services-location:$googleServicesVersion"
+ implementation "com.google.maps.android:android-maps-utils:0.5"
- // UI tests
- androidTestCompile "junit:junit:$junitVersion"
- androidTestCompile "com.android.support.test:runner:$supportTestsLibVersion"
- androidTestCompile "com.android.support.test:rules:$supportTestsLibVersion"
- androidTestCompile "com.android.support:support-annotations:$supportVersion"
- androidTestCompile "com.android.support.test.espresso:espresso-intents:$espressoVersion"
- androidTestCompile "com.android.support.test.espresso:espresso-core:$espressoVersion"
- compile 'com.github.bumptech.glide:glide:3.7.0'
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation 'com.github.bumptech.glide:glide:3.7.0'
// RxJava
- compile 'io.reactivex:rxjava:1.1.6'
- compile 'io.reactivex:rxandroid:1.2.1'
- compile 'com.tbruyelle.rxpermissions:rxpermissions:0.8.0@aar'
+ implementation 'io.reactivex:rxjava:1.1.6'
+ implementation 'io.reactivex:rxandroid:1.2.1'
+ implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.8.0@aar'
+ implementation "androidx.core:core-ktx:1.0.2"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
}
diff --git a/app/src/androidTest/java/com/mlsdev/mapsappsample/MainActivityTest.java b/app/src/androidTest/java/com/mlsdev/mapsappsample/MainActivityTest.java
deleted file mode 100644
index c303722..0000000
--- a/app/src/androidTest/java/com/mlsdev/mapsappsample/MainActivityTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.action.ViewActions;
-import android.support.test.espresso.intent.Intents;
-import android.support.test.espresso.intent.matcher.IntentMatchers;
-import android.support.test.espresso.intent.rule.IntentsTestRule;
-import android.support.test.espresso.matcher.ViewMatchers;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-public class MainActivityTest {
-
- @Rule
- public IntentsTestRule rule = new IntentsTestRule<>(MainActivity.class);
-
- @Test
- public void testStartPlacePickerActivity() {
- Espresso.onView(ViewMatchers.withId(R.id.btn_pick_a_place))
- .perform(ViewActions.click());
-
- Intents.intended(IntentMatchers.hasComponent(PlacePickerActivity.class.getName()));
- }
-
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d6173ad..f38a3d3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,16 +1,23 @@
+
+
+ android:fullBackupContent="true"
+ android:theme="@style/AppTheme"
+ tools:ignore="GoogleAppIndexingWarning">
@@ -33,8 +40,22 @@
android:name=".StaticImageMapActivity"
android:label="@string/title_static_map" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/BaseActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/BaseActivity.java
deleted file mode 100644
index 4b4fbc6..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/BaseActivity.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
-import android.view.MenuItem;
-
-public class BaseActivity extends AppCompatActivity {
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home)
- finish();
-
- return super.onOptionsItemSelected(item);
- }
-
- protected void showBackButton(boolean show) {
- if (getSupportActionBar() != null) {
- getSupportActionBar().setHomeButtonEnabled(show);
- getSupportActionBar().setDisplayHomeAsUpEnabled(show);
- }
- }
-
- public void showAlertDialog(@Nullable String title, @NonNull String message) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialog);
- builder.setTitle(title)
- .setMessage(message)
- .setPositiveButton("OK", null);
-
- builder.create().show();
- }
-
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/BaseActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/BaseActivity.kt
new file mode 100644
index 0000000..62c0542
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/BaseActivity.kt
@@ -0,0 +1,32 @@
+package com.mlsdev.mapsappsample
+
+import android.view.MenuItem
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+
+abstract class BaseActivity : AppCompatActivity() {
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home)
+ finish()
+
+ return super.onOptionsItemSelected(item)
+ }
+
+ protected fun showBackButton(show: Boolean) {
+ if (supportActionBar != null) {
+ supportActionBar!!.setHomeButtonEnabled(show)
+ supportActionBar!!.setDisplayHomeAsUpEnabled(show)
+ }
+ }
+
+ fun showAlertDialog(title: String?, message: String) {
+ val builder = AlertDialog.Builder(this, R.style.AlertDialog)
+ builder.setTitle(title)
+ .setMessage(message)
+ .setPositiveButton("OK", null)
+
+ builder.create().show()
+ }
+
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/BasicMapActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/BasicMapActivity.java
deleted file mode 100644
index 06dd5ca..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/BasicMapActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.os.Bundle;
-
-import com.google.android.gms.maps.GoogleMap;
-import com.google.android.gms.maps.OnMapReadyCallback;
-import com.google.android.gms.maps.SupportMapFragment;
-import com.google.android.gms.maps.model.BitmapDescriptorFactory;
-import com.google.android.gms.maps.model.LatLng;
-import com.google.android.gms.maps.model.MarkerOptions;
-
-public class BasicMapActivity extends BaseActivity implements OnMapReadyCallback {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_basic_map);
- showBackButton(true);
-
- SupportMapFragment mapFragment =
- (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
- mapFragment.getMapAsync(this);
- }
-
- @Override
- public void onMapReady(GoogleMap googleMap) {
- googleMap.addMarker(new MarkerOptions()
- .icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher))
- .position(new LatLng(40.759035, -73.980115)).title("MLSDev"));
- }
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/BasicMapActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/BasicMapActivity.kt
new file mode 100644
index 0000000..5557084
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/BasicMapActivity.kt
@@ -0,0 +1,28 @@
+package com.mlsdev.mapsappsample
+
+import android.os.Bundle
+
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.BitmapDescriptorFactory
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.MarkerOptions
+
+class BasicMapActivity : BaseActivity(), OnMapReadyCallback {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_basic_map)
+ showBackButton(true)
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
+ mapFragment!!.getMapAsync(this)
+ }
+
+ override fun onMapReady(googleMap: GoogleMap) {
+ googleMap.addMarker(MarkerOptions()
+ .icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher))
+ .position(LatLng(40.759035, -73.980115)).title("MLSDev"))
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/GoogleApiClientActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/GoogleApiClientActivity.java
deleted file mode 100644
index ff1d44f..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/GoogleApiClientActivity.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.app.Dialog;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GooglePlayServicesUtil;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.location.places.Places;
-
-public class GoogleApiClientActivity extends BaseActivity implements GoogleApiClient.ConnectionCallbacks,
- GoogleApiClient.OnConnectionFailedListener {
-
- protected GoogleApiClient googleApiClient;
- private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
- public static final int PLACE_PICKER_REQUEST = 1;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- buildGoogleApiClient();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- googleApiClient.connect();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- googleApiClient.disconnect();
- }
-
- private void buildGoogleApiClient() {
- googleApiClient = new GoogleApiClient
- .Builder(this)
- .addApi(Places.GEO_DATA_API)
- .addApi(Places.PLACE_DETECTION_API)
- .addConnectionCallbacks(this)
- .addOnConnectionFailedListener(this)
- .build();
- }
-
- private void showErrorDialog(int errorCode) {
-
- // Get the error dialog from Google Play services
- Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
- errorCode,
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
-
- // If Google Play services can provide an error dialog
- if (errorDialog != null)
- errorDialog.show();
- }
-
- @Override
- public void onConnected(@Nullable Bundle bundle) {
- Log.d("myLogs", "onConnected()");
- }
-
- @Override
- public void onConnectionSuspended(int i) {
- Log.d("myLogs", "onConnectionSuspended()");
- }
-
- @Override
- public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
- Log.d("myLogs", "onConnectionFailed() \r\n Error: " + connectionResult.getErrorMessage());
- if (connectionResult.hasResolution()) {
- try {
- // Start an Activity that tries to resolve the error
- connectionResult.startResolutionForResult(
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
- /*
- * Thrown if Google Play services canceled the original
- * PendingIntent
- */
- } catch (IntentSender.SendIntentException e) {
- e.printStackTrace();
- }
- } else {
- /*
- * If no resolution is available, display a dialog to the
- * user with the error.
- */
- showErrorDialog(connectionResult.getErrorCode());
- }
- }
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/GoogleApiClientActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/GoogleApiClientActivity.kt
new file mode 100644
index 0000000..691dcd5
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/GoogleApiClientActivity.kt
@@ -0,0 +1,88 @@
+package com.mlsdev.mapsappsample
+
+import android.content.IntentSender
+import android.os.Bundle
+import android.util.Log
+import com.google.android.gms.common.ConnectionResult
+import com.google.android.gms.common.GooglePlayServicesUtil
+import com.google.android.gms.common.api.GoogleApiClient
+import com.google.android.gms.location.places.Places
+
+abstract class GoogleApiClientActivity : BaseActivity(), GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+ protected var googleApiClient: GoogleApiClient? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ buildGoogleApiClient()
+ }
+
+ override fun onStart() {
+ super.onStart()
+ googleApiClient?.connect()
+ }
+
+ override fun onStop() {
+ super.onStop()
+ googleApiClient?.disconnect()
+ }
+
+ private fun buildGoogleApiClient() {
+ googleApiClient = GoogleApiClient.Builder(this)
+ .addApi(Places.GEO_DATA_API)
+ .addApi(Places.PLACE_DETECTION_API)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .build()
+ }
+
+ private fun showErrorDialog(errorCode: Int) {
+
+ // Get the error dialog from Google Play services
+ val errorDialog = GooglePlayServicesUtil.getErrorDialog(
+ errorCode,
+ this,
+ CONNECTION_FAILURE_RESOLUTION_REQUEST)
+
+ // If Google Play services can provide an error dialog
+ errorDialog?.show()
+ }
+
+ override fun onConnected(bundle: Bundle?) {
+ Log.d("myLogs", "onConnected()")
+ }
+
+ override fun onConnectionSuspended(i: Int) {
+ Log.d("myLogs", "onConnectionSuspended()")
+ }
+
+ override fun onConnectionFailed(connectionResult: ConnectionResult) {
+ Log.d("myLogs", "onConnectionFailed() \r\n Error: " + connectionResult.errorMessage!!)
+ if (connectionResult.hasResolution()) {
+ try {
+ // Start an Activity that tries to resolve the error
+ connectionResult.startResolutionForResult(
+ this,
+ CONNECTION_FAILURE_RESOLUTION_REQUEST)
+ /*
+ * Thrown if Google Play services canceled the original
+ * PendingIntent
+ */
+ } catch (e: IntentSender.SendIntentException) {
+ e.printStackTrace()
+ }
+
+ } else {
+ /*
+ * If no resolution is available, display a dialog to the
+ * user with the error.
+ */
+ showErrorDialog(connectionResult.errorCode)
+ }
+ }
+
+ companion object {
+ private const val CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000
+ const val PLACE_PICKER_REQUEST = 1
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/MainActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/MainActivity.java
deleted file mode 100644
index dea6f58..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/MainActivity.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.Manifest;
-import android.content.Intent;
-import android.databinding.DataBindingUtil;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-
-import com.mlsdev.mapsappsample.databinding.ActivityMainBinding;
-import com.tbruyelle.rxpermissions.RxPermissions;
-
-public class MainActivity extends AppCompatActivity {
- private ActivityMainBinding binding;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
- binding.setViewModel(new MainViewModel());
- getLocationPermission();
- }
-
- private void getLocationPermission() {
- RxPermissions.getInstance(this)
- .request(Manifest.permission.ACCESS_FINE_LOCATION)
- .subscribe(granted -> {
- if (!granted)
- MainActivity.this.finish();
- });
- }
-
-
- public class MainViewModel {
-
- public void onPickPlaceButtonClick(View view) {
- startActivity(new Intent(MainActivity.this, PlacePickerActivity.class));
- }
-
- public void onMapButtonClick(View view) {
- startActivity(new Intent(MainActivity.this, BasicMapActivity.class));
- }
-
- public void onStaticImageButtonClick(View view) {
- startActivity(new Intent(MainActivity.this, StaticImageMapActivity.class));
- }
-
- public void onPlaceSuggestionsButtonClick(View view) {
- startActivity(new Intent(MainActivity.this, PlaceSuggestionsActivity.class));
- }
-
- }
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/MainActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/MainActivity.kt
new file mode 100644
index 0000000..4b6a745
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/MainActivity.kt
@@ -0,0 +1,51 @@
+package com.mlsdev.mapsappsample
+
+import android.Manifest
+import android.content.Intent
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.mlsdev.mapsappsample.databinding.ActivityMainBinding
+import com.mlsdev.mapsappsample.markerclustering.MarkerClusteringActivity
+import com.mlsdev.mapsappsample.placesuggestions.PlaceSuggestionsActivity
+import com.tbruyelle.rxpermissions.RxPermissions
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
+
+ binding.btnOpenMap.setOnClickListener {
+ startActivity(Intent(this@MainActivity, BasicMapActivity::class.java))
+ }
+
+ binding.btnPickAPlace.setOnClickListener {
+ startActivity(Intent(this@MainActivity, PlacePickerActivity::class.java))
+ }
+
+ binding.btnPlaceSuggestions.setOnClickListener {
+ startActivity(Intent(this@MainActivity, PlaceSuggestionsActivity::class.java))
+ }
+
+ binding.btnStaticImage.setOnClickListener {
+ startActivity(Intent(this@MainActivity, StaticImageMapActivity::class.java))
+ }
+
+ binding.buttonClusterMarkers.setOnClickListener {
+ startActivity(Intent(this, MarkerClusteringActivity::class.java))
+ }
+
+ getLocationPermission()
+ }
+
+ private fun getLocationPermission() {
+ RxPermissions.getInstance(this)
+ .request(Manifest.permission.ACCESS_FINE_LOCATION)
+ .subscribe { granted ->
+ if (!granted)
+ this@MainActivity.finish()
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/PlacePickerActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/PlacePickerActivity.java
deleted file mode 100644
index ff01c14..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/PlacePickerActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.content.Intent;
-import android.databinding.DataBindingUtil;
-import android.databinding.ObservableField;
-import android.os.Bundle;
-import android.view.View;
-
-import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
-import com.google.android.gms.common.GooglePlayServicesRepairableException;
-import com.google.android.gms.location.places.Place;
-import com.google.android.gms.location.places.ui.PlacePicker;
-import com.mlsdev.mapsappsample.databinding.ActivityPlacePickerBinding;
-
-public class PlacePickerActivity extends GoogleApiClientActivity {
- private ActivityPlacePickerBinding binding;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- showBackButton(true);
- binding = DataBindingUtil.setContentView(this, R.layout.activity_place_picker);
- binding.setViewModel(new PlacePickerViewModel());
- startPlacesPickerActivity();
- }
-
- private void startPlacesPickerActivity() {
- PlacePicker.IntentBuilder intentBuilder = new PlacePicker.IntentBuilder();
- try {
- startActivityForResult(intentBuilder.build(this), PLACE_PICKER_REQUEST);
- } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == PLACE_PICKER_REQUEST) {
- if (resultCode == RESULT_OK) {
- Place place = PlacePicker.getPlace(this, data);
- binding.getViewModel().setData(place);
- }
- }
- }
-
- public class PlacePickerViewModel {
- public final ObservableField placeName;
- public final ObservableField placeAddress;
-
- public PlacePickerViewModel() {
- placeName = new ObservableField<>();
- placeAddress = new ObservableField<>();
- }
-
- public void onPickPlaceButtonClick(View view) {
- startPlacesPickerActivity();
- }
-
- public void setData(Place place) {
- placeName.set(place.getName().toString());
- placeAddress.set(place.getAddress().toString());
- }
- }
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/PlacePickerActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/PlacePickerActivity.kt
new file mode 100644
index 0000000..bd778fe
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/PlacePickerActivity.kt
@@ -0,0 +1,59 @@
+package com.mlsdev.mapsappsample
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ObservableField
+
+import com.google.android.gms.common.GooglePlayServicesNotAvailableException
+import com.google.android.gms.common.GooglePlayServicesRepairableException
+import com.google.android.gms.location.places.Place
+import com.google.android.gms.location.places.ui.PlacePicker
+import com.mlsdev.mapsappsample.databinding.ActivityPlacePickerBinding
+
+class PlacePickerActivity : GoogleApiClientActivity() {
+ private var binding: ActivityPlacePickerBinding? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ showBackButton(true)
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_place_picker)
+ binding!!.viewModel = PlacePickerViewModel()
+ startPlacesPickerActivity()
+
+ binding!!.btnPickAPlace.setOnClickListener { startPlacesPickerActivity() }
+ }
+
+ private fun startPlacesPickerActivity() {
+ val intentBuilder = PlacePicker.IntentBuilder()
+ try {
+ startActivityForResult(intentBuilder.build(this), PLACE_PICKER_REQUEST)
+ } catch (e: GooglePlayServicesRepairableException) {
+ e.printStackTrace()
+ } catch (e: GooglePlayServicesNotAvailableException) {
+ e.printStackTrace()
+ }
+
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (requestCode == PLACE_PICKER_REQUEST) {
+ if (resultCode == Activity.RESULT_OK) {
+ val place = PlacePicker.getPlace(this, data!!)
+ binding!!.viewModel?.setData(place)
+ }
+ }
+ }
+
+ inner class PlacePickerViewModel {
+ val placeName: ObservableField = ObservableField()
+ val placeAddress: ObservableField = ObservableField()
+
+ fun setData(place: Place) {
+ placeName.set(place.name.toString())
+ placeAddress.set(place.address!!.toString())
+ }
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/PlaceSuggestionsActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/PlaceSuggestionsActivity.java
deleted file mode 100644
index e93069d..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/PlaceSuggestionsActivity.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.os.Bundle;
-import android.util.Log;
-
-import com.google.android.gms.common.api.Status;
-import com.google.android.gms.location.places.Place;
-import com.google.android.gms.location.places.ui.PlaceAutocompleteFragment;
-import com.google.android.gms.location.places.ui.PlaceSelectionListener;
-
-public class PlaceSuggestionsActivity extends GoogleApiClientActivity {
-
- private static final String TAG = "places";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_place_suggestions);
- showBackButton(true);
-
- PlaceAutocompleteFragment autocompleteFragment = (PlaceAutocompleteFragment)
- getFragmentManager().findFragmentById(R.id.place_autocomplete_fragment);
-
- autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
- @Override
- public void onPlaceSelected(Place place) {
- // TODO: Get info about the selected place.
- Log.i(TAG, "Place: " + place.getName());
- }
-
- @Override
- public void onError(Status status) {
- // TODO: Handle the error.
- Log.i(TAG, "An error occurred: " + status);
- }
- });
-
- }
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/StaticImageMapActivity.java b/app/src/main/java/com/mlsdev/mapsappsample/StaticImageMapActivity.java
deleted file mode 100644
index b2bd5aa..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/StaticImageMapActivity.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import android.databinding.DataBindingUtil;
-import android.databinding.ObservableField;
-import android.os.Bundle;
-import android.support.v4.util.ArrayMap;
-import android.view.View;
-
-import com.bumptech.glide.Glide;
-import com.mlsdev.mapsappsample.databinding.ActivityStaticImageMapBinding;
-import com.mlsdev.mapsappsample.listener.OnSeekBarListener;
-import com.mlsdev.mapsappsample.url.UrlBuilder;
-
-import java.util.Map;
-
-import static com.mlsdev.mapsappsample.url.Constants.CENTER;
-import static com.mlsdev.mapsappsample.url.Constants.KEY;
-import static com.mlsdev.mapsappsample.url.Constants.MAP_TYPE;
-import static com.mlsdev.mapsappsample.url.Constants.MARKERS;
-import static com.mlsdev.mapsappsample.url.Constants.SCALE;
-import static com.mlsdev.mapsappsample.url.Constants.SIZE;
-import static com.mlsdev.mapsappsample.url.Constants.ZOOM;
-
-
-public class StaticImageMapActivity extends BaseActivity {
- private ActivityStaticImageMapBinding binding;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- binding = DataBindingUtil.setContentView(this, R.layout.activity_static_image_map);
- binding.setViewModel(new ViewModel());
- showBackButton(true);
- }
-
- public class ViewModel {
- public final ObservableField zoomProgress;
- public final ObservableField scaleProgress;
-
- public ViewModel() {
- zoomProgress = new ObservableField<>("0");
- scaleProgress = new ObservableField<>("0");
- binding.sbZoom.setOnSeekBarChangeListener(new OnSeekBarListener(zoomProgress));
- binding.sbScale.setOnSeekBarChangeListener(new OnSeekBarListener(scaleProgress));
- }
-
- public void onGetImageMapButtonClick(View view) {
- getStaticImageMap();
- }
-
- private void getStaticImageMap() {
-
- if (binding.etCenter.getText().toString().isEmpty()) {
- showAlertDialog(null, "Center can\'t be blank!");
- return;
- }
-
- String mapType = (String) binding.spMapTypes.getSelectedItem();
-
- Map params = new ArrayMap<>();
- params.put(CENTER, binding.etCenter.getText().toString());
- params.put(ZOOM, String.valueOf(binding.sbZoom.getProgress()));
- params.put(SIZE, "550x300");
- params.put(MAP_TYPE, mapType);
- params.put(SCALE, String.valueOf(binding.sbScale.getProgress()));
- if (binding.cbAddMarker.isChecked()) {
- String markerPlace = binding.etCenter.getText().toString();
- // markerPlace can be defined like a lat lon pair (45.523908,-122.669866)
- params.put(MARKERS, "size:large|color:blue|label:A|" + markerPlace);
- }
- params.put(KEY, getString(R.string.google_app_api_key));
- Glide.with(StaticImageMapActivity.this)
- .load(UrlBuilder.buildUrl(params))
- .crossFade()
- .into(binding.ivStaticMap);
- }
-
- }
-
-
-
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/StaticImageMapActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/StaticImageMapActivity.kt
new file mode 100644
index 0000000..ec9a5be
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/StaticImageMapActivity.kt
@@ -0,0 +1,73 @@
+package com.mlsdev.mapsappsample
+
+import android.os.Bundle
+import android.util.ArrayMap
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ObservableField
+import com.bumptech.glide.Glide
+import com.mlsdev.mapsappsample.databinding.ActivityStaticImageMapBinding
+import com.mlsdev.mapsappsample.listener.OnSeekBarListener
+import com.mlsdev.mapsappsample.url.Constants.CENTER
+import com.mlsdev.mapsappsample.url.Constants.KEY
+import com.mlsdev.mapsappsample.url.Constants.MAP_TYPE
+import com.mlsdev.mapsappsample.url.Constants.MARKERS
+import com.mlsdev.mapsappsample.url.Constants.SCALE
+import com.mlsdev.mapsappsample.url.Constants.SIZE
+import com.mlsdev.mapsappsample.url.Constants.ZOOM
+import com.mlsdev.mapsappsample.url.UrlBuilder
+
+
+class StaticImageMapActivity : BaseActivity() {
+ private var binding: ActivityStaticImageMapBinding? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_static_image_map)
+ binding!!.viewModel = ViewModel()
+ showBackButton(true)
+ }
+
+ inner class ViewModel {
+ val zoomProgress: ObservableField = ObservableField("0")
+ val scaleProgress: ObservableField = ObservableField("0")
+
+ init {
+ binding!!.sbZoom.setOnSeekBarChangeListener(OnSeekBarListener(zoomProgress))
+ binding!!.sbScale.setOnSeekBarChangeListener(OnSeekBarListener(scaleProgress))
+ }
+
+ fun onGetImageMapButtonClick() {
+ getStaticImageMap()
+ }
+
+ private fun getStaticImageMap() {
+
+ if (binding!!.etCenter.text!!.toString().isEmpty()) {
+ showAlertDialog(null, "Center can\'t be blank!")
+ return
+ }
+
+ val mapType = binding!!.spMapTypes.selectedItem as String
+
+ val params = ArrayMap()
+ params[CENTER] = binding!!.etCenter.text!!.toString()
+ params[ZOOM] = binding!!.sbZoom.progress.toString()
+ params[SIZE] = "550x300"
+ params[MAP_TYPE] = mapType
+ params[SCALE] = binding!!.sbScale.progress.toString()
+ if (binding!!.cbAddMarker.isChecked) {
+ val markerPlace = binding!!.etCenter.text!!.toString()
+ // markerPlace can be defined like a lat lon pair (45.523908,-122.669866)
+ params[MARKERS] = "size:large|color:blue|label:A|$markerPlace"
+ }
+ params[KEY] = getString(R.string.google_app_api_key)
+ Glide.with(this@StaticImageMapActivity)
+ .load(UrlBuilder.buildUrl(params))
+ .crossFade()
+ .into(binding!!.ivStaticMap)
+ }
+
+ }
+
+
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/listener/OnSeekBarListener.java b/app/src/main/java/com/mlsdev/mapsappsample/listener/OnSeekBarListener.java
deleted file mode 100644
index 8863eea..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/listener/OnSeekBarListener.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.mlsdev.mapsappsample.listener;
-
-import android.databinding.ObservableField;
-import android.widget.SeekBar;
-
-public class OnSeekBarListener implements SeekBar.OnSeekBarChangeListener {
- private final ObservableField progress;
-
- public OnSeekBarListener(ObservableField progress) {
- this.progress = progress;
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
- progress.set(String.valueOf(seekBar.getProgress()));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/listener/OnSeekBarListener.kt b/app/src/main/java/com/mlsdev/mapsappsample/listener/OnSeekBarListener.kt
new file mode 100644
index 0000000..127add4
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/listener/OnSeekBarListener.kt
@@ -0,0 +1,16 @@
+package com.mlsdev.mapsappsample.listener
+
+import android.widget.SeekBar
+
+import androidx.databinding.ObservableField
+
+class OnSeekBarListener(private val progress: ObservableField) : SeekBar.OnSeekBarChangeListener {
+
+ override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
+ progress.set(seekBar.progress.toString())
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {}
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {}
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerClusteringActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerClusteringActivity.kt
new file mode 100644
index 0000000..4f17a9c
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerClusteringActivity.kt
@@ -0,0 +1,215 @@
+package com.mlsdev.mapsappsample.markerclustering
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
+import android.os.Bundle
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.Observer
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.BitmapDescriptorFactory
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.google.android.gms.maps.model.MarkerOptions
+import com.google.maps.android.clustering.Cluster
+import com.google.maps.android.clustering.ClusterManager
+import com.google.maps.android.clustering.view.DefaultClusterRenderer
+import com.google.maps.android.ui.IconGenerator
+import com.mlsdev.mapsappsample.BaseActivity
+import com.mlsdev.mapsappsample.R
+import com.mlsdev.mapsappsample.databinding.ActivityMarkerClusteringBinding
+import com.mlsdev.mapsappsample.databinding.LayoutMarkersClusterBinding
+import com.mlsdev.mapsappsample.utils.DrawableToBitmapDecoder
+import com.mlsdev.mapsappsample.utils.MapUtils
+import com.tbruyelle.rxpermissions.RxPermissions
+
+class MarkerClusteringActivity :
+ BaseActivity(),
+ OnMapReadyCallback,
+ LocationListener,
+ ClusterManager.OnClusterClickListener,
+ ClusterManager.OnClusterItemClickListener {
+
+ companion object {
+ const val MIN_CLUSTER_SIZE = 1
+ }
+
+ lateinit var binding: ActivityMarkerClusteringBinding
+ lateinit var viewModel: MarkerClusteringViewModel
+ lateinit var clusterManager: ClusterManager
+ lateinit var markerRenderer: PrimaryMarkerRenderer
+ private var googleMap: GoogleMap? = null
+ private var lastLatLng: LatLng? = null
+
+ private val markers = hashMapOf(
+ MarkerItem.Type.HEALTHCARE_CENTER to R.drawable.ic_marker_healthcare_center,
+ MarkerItem.Type.LABORATORY to R.drawable.ic_marker_laboratory,
+ MarkerItem.Type.VACCINATION to R.drawable.ic_marker_vaccination)
+
+ private val markersLarge = hashMapOf(
+ MarkerItem.Type.HEALTHCARE_CENTER to R.drawable.ic_marker_healthcare_center_large,
+ MarkerItem.Type.LABORATORY to R.drawable.ic_marker_laboratory_large,
+ MarkerItem.Type.VACCINATION to R.drawable.ic_marker_vaccination_large)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_marker_clustering)
+ viewModel = MarkerClusteringViewModel()
+ binding.viewModel = viewModel
+ observeMarkers()
+ setupMap()
+ }
+
+ private fun setupMap() {
+ val mapFragment: SupportMapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ mapFragment.getMapAsync(this)
+ }
+
+ override fun onMapReady(map: GoogleMap?) {
+ googleMap = googleMap ?: map
+ googleMap?.let {
+ it.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.503186, -0.126446), 10f))
+ clusterManager = ClusterManager(this, it)
+ markerRenderer = PrimaryMarkerRenderer(it, clusterManager)
+ clusterManager.renderer = markerRenderer
+ clusterManager.setOnClusterClickListener(this)
+ clusterManager.setOnClusterItemClickListener(this)
+ it.setOnCameraIdleListener(clusterManager)
+ it.setOnMarkerClickListener(clusterManager)
+ getLocationPermission()
+ setupMarkers()
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun getLocationPermission() {
+ RxPermissions.getInstance(this)
+ .request(Manifest.permission.ACCESS_FINE_LOCATION)
+ .subscribe { granted ->
+ if (granted) {
+ googleMap?.isMyLocationEnabled = true
+ val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1L, 1f, this)
+ }
+ }
+ }
+
+ private fun setupMarkers() {
+ val inputStream = resources.openRawResource(R.raw.radar_search)
+ viewModel.prepareMarkers(inputStream)
+ }
+
+ private fun observeMarkers() {
+ viewModel.markers.observe(this, Observer { markers ->
+ clusterManager.addItems(markers)
+ })
+ }
+
+ inner class PrimaryMarkerRenderer(map: GoogleMap, clusterManager: ClusterManager)
+ : DefaultClusterRenderer(applicationContext, map, clusterManager) {
+
+ private val clusterIconGenerator = IconGenerator(applicationContext)
+ private val clusterBinding = LayoutMarkersClusterBinding.inflate(layoutInflater)
+ var prevSelectedItem: MarkerItem? = null
+
+ override fun onBeforeClusterItemRendered(item: MarkerItem, markerOptions: MarkerOptions) {
+ val drawableRes = markers[item.type] ?: R.drawable.ic_marker
+ val bitmap = DrawableToBitmapDecoder.getBitmap(applicationContext, drawableRes)
+ markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap))
+ }
+
+ override fun onBeforeClusterRendered(cluster: Cluster?, markerOptions: MarkerOptions?) {
+ clusterIconGenerator.setBackground(ContextCompat.getDrawable(applicationContext, android.R.color.transparent))
+ clusterIconGenerator.setContentView(clusterBinding.root)
+ clusterBinding.markersCount.text = cluster?.size.toString()
+ markerOptions?.icon(BitmapDescriptorFactory.fromBitmap(clusterIconGenerator.makeIcon()))
+ }
+
+ override fun shouldRenderAsCluster(cluster: Cluster?): Boolean {
+ return cluster?.size ?: 0 > MIN_CLUSTER_SIZE
+ }
+
+ }
+
+ override fun onClusterClick(cluster: Cluster): Boolean {
+
+ // Zoom in the cluster. Need to create LatLngBounds and including all the cluster items
+ // inside of bounds, then animate to center of the bounds.
+
+ // Create the builder to collect all essential cluster items for the bounds.
+ val builder = LatLngBounds.builder()
+ for (item in cluster.items) {
+ builder.include(item.position)
+ }
+ // Get the LatLngBounds
+ val bounds = builder.build()
+
+ // Animate camera to the bounds
+ try {
+ googleMap?.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ return true
+ }
+
+ override fun onClusterItemClick(markerItem: MarkerItem?): Boolean {
+ val prevItem = markerRenderer.prevSelectedItem
+ val prevMarker = markerRenderer.getMarker(prevItem)
+
+ if (prevItem != null && prevMarker != null) {
+ val drawableRes = markers[prevItem.type] ?: R.drawable.ic_marker
+ val bitmap = DrawableToBitmapDecoder.getBitmap(applicationContext, drawableRes)
+ prevMarker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap))
+ }
+
+ markerRenderer.getMarker(markerItem)?.let { marker ->
+ markerRenderer.prevSelectedItem = markerItem
+ val drawableRes = markersLarge[markerItem?.type] ?: R.drawable.ic_marker
+ val bitmap = DrawableToBitmapDecoder.getBitmap(applicationContext, drawableRes)
+ marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap))
+ }
+
+ showMarkerInfoDialog(markerItem)
+
+ return true
+ }
+
+ private fun showMarkerInfoDialog(markerItem: MarkerItem?) {
+ val currentLatLng = lastLatLng
+
+ if (currentLatLng != null && markerItem != null) {
+ val markerLatLng = LatLng(markerItem.lat, markerItem.lng)
+ val distance = MapUtils.calculationByDistanceInKm(currentLatLng, markerLatLng)
+ val message = getString(R.string.template_marker_info, markerItem.type.toString(), distance.toString())
+
+ AlertDialog.Builder(this)
+ .setTitle(R.string.label_marker_info)
+ .setMessage(message)
+ .setPositiveButton(R.string.label_close, null)
+ .create()
+ .show()
+ }
+ }
+
+ override fun onLocationChanged(location: Location?) {
+ location?.let { lastLatLng = LatLng(it.latitude, it.longitude) }
+ }
+
+ override fun onProviderDisabled(provider: String?) {
+ }
+
+ override fun onProviderEnabled(provider: String?) {
+ }
+
+ override fun onStatusChanged(provider: String?, p1: Int, p2: Bundle?) {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerClusteringViewModel.kt b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerClusteringViewModel.kt
new file mode 100644
index 0000000..22f5122
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerClusteringViewModel.kt
@@ -0,0 +1,14 @@
+package com.mlsdev.mapsappsample.markerclustering
+
+import androidx.lifecycle.MutableLiveData
+import java.io.InputStream
+
+class MarkerClusteringViewModel {
+ val markers = MutableLiveData>()
+
+ fun prepareMarkers(inputStream: InputStream) {
+ val markers = MarkerItemReader.read(inputStream)
+ this.markers.postValue(markers)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerItem.kt b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerItem.kt
new file mode 100644
index 0000000..b0fb1cf
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerItem.kt
@@ -0,0 +1,31 @@
+package com.mlsdev.mapsappsample.markerclustering
+
+import com.google.android.gms.maps.model.LatLng
+import com.google.gson.annotations.SerializedName
+import com.google.maps.android.clustering.ClusterItem
+
+data class MarkerItem(
+ @SerializedName("lat")
+ val lat: Double,
+ @SerializedName("lng")
+ val lng: Double,
+ @SerializedName("title")
+ val markerTitle: String?,
+ @SerializedName("snippet")
+ val markerDescriptor: String?
+) : ClusterItem {
+
+ var type: Type = Type.LABORATORY
+
+ override fun getSnippet(): String = markerDescriptor ?: ""
+
+ override fun getTitle(): String = markerTitle ?: ""
+
+ override fun getPosition(): LatLng = LatLng(lat, lng)
+
+ enum class Type {
+ VACCINATION,
+ LABORATORY,
+ HEALTHCARE_CENTER
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerItemReader.kt b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerItemReader.kt
new file mode 100644
index 0000000..fd898ef
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/markerclustering/MarkerItemReader.kt
@@ -0,0 +1,44 @@
+package com.mlsdev.mapsappsample.markerclustering
+
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import org.json.JSONException
+import java.io.InputStream
+import java.util.*
+import kotlin.collections.ArrayList
+import kotlin.random.Random
+
+
+object MarkerItemReader {
+
+ /*
+ * This matches only once in whole input,
+ * so Scanner.next returns whole InputStream as a String.
+ * http://stackoverflow.com/a/5445161/2183804
+ */
+ private const val REGEX_INPUT_BOUNDARY_BEGINNING = "\\A"
+
+ @Throws(JSONException::class)
+ fun read(inputStream: InputStream): List {
+ val random = Random(1989)
+ val itemTypes = arrayListOf(
+ MarkerItem.Type.LABORATORY,
+ MarkerItem.Type.VACCINATION,
+ MarkerItem.Type.HEALTHCARE_CENTER
+ )
+ val items = ArrayList()
+ val json = Scanner(inputStream).useDelimiter(REGEX_INPUT_BOUNDARY_BEGINNING).next()
+ val gson = Gson()
+
+ val listType = object : TypeToken>() {}.type
+
+ items.addAll(gson.fromJson(json, listType))
+
+ for (item in items) {
+ item.type = itemTypes[random.nextInt(0, 3)]
+ }
+
+ return items
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceEntity.kt b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceEntity.kt
new file mode 100644
index 0000000..e25a19d
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceEntity.kt
@@ -0,0 +1,3 @@
+package com.mlsdev.mapsappsample.placesuggestions
+
+class PlaceEntity
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceSuggestionsActivity.kt b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceSuggestionsActivity.kt
new file mode 100644
index 0000000..0407b77
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceSuggestionsActivity.kt
@@ -0,0 +1,83 @@
+package com.mlsdev.mapsappsample.placesuggestions
+
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.Log
+import android.view.View
+
+import androidx.databinding.DataBindingUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.location.places.Place
+import com.google.android.gms.location.places.ui.PlaceAutocompleteFragment
+import com.google.android.gms.location.places.ui.PlaceSelectionListener
+import com.mlsdev.mapsappsample.GoogleApiClientActivity
+import com.mlsdev.mapsappsample.R
+import com.mlsdev.mapsappsample.databinding.ActivityPlaceSuggestionsBinding
+
+class PlaceSuggestionsActivity : GoogleApiClientActivity() {
+ lateinit var binding: ActivityPlaceSuggestionsBinding
+ lateinit var viewModel: PlacesViewModel
+ lateinit var placesAdapter: PlacesAdapter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ showBackButton(true)
+
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_place_suggestions)
+ viewModel = PlacesViewModel(googleApiClient)
+ binding.viewModel = viewModel
+ binding.etSearch.addTextChangedListener(OnSearchTextChangedListener())
+
+ placesAdapter = PlacesAdapter { viewModel.onPlaceSelected(it) }
+
+ binding.rvPlacesSuggestions.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
+ binding.rvPlacesSuggestions.adapter = placesAdapter
+
+ val autocompleteFragment = fragmentManager.findFragmentById(R.id.place_autocomplete_fragment) as PlaceAutocompleteFragment
+
+ autocompleteFragment.setOnPlaceSelectedListener(object : PlaceSelectionListener {
+ override fun onPlaceSelected(place: Place) {
+ // TODO: Get info about the selected place.
+ Log.i(TAG, "Place: " + place.name)
+ }
+
+ override fun onError(status: Status) {
+ // TODO: Handle the error.
+ Log.i(TAG, "An error occurred: $status")
+ }
+ })
+
+ binding.btnClearSearchText.setOnClickListener { binding.etSearch.setText("") }
+ }
+
+ inner class OnSearchTextChangedListener : TextWatcher {
+
+ override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
+
+ }
+
+ override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
+
+ }
+
+ override fun afterTextChanged(editable: Editable) {
+ binding.btnClearSearchText.visibility = if (editable.isNotEmpty()) View.VISIBLE else View.INVISIBLE
+
+ if (editable.isEmpty())
+ placesAdapter.clearData()
+
+ if (editable.length > 2 && !viewModel.isItemSelected)
+ viewModel.searchPlaces(editable.toString())
+
+ binding.etSearch.setSelection(editable.length)
+ }
+ }
+
+ companion object {
+ private const val TAG = "places"
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceViewModel.kt b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceViewModel.kt
new file mode 100644
index 0000000..8bd9f66
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlaceViewModel.kt
@@ -0,0 +1,15 @@
+package com.mlsdev.mapsappsample.placesuggestions
+
+import android.util.Log
+import android.view.View
+
+import androidx.databinding.ObservableField
+
+class PlaceViewModel(private val onItemClickListener: (place: String) -> Unit) {
+ val placeFullText: ObservableField = ObservableField()
+
+ fun onClickView(view: View) {
+ Log.d("PlacesItem", placeFullText.get())
+ onItemClickListener(placeFullText.get()!!)
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlacesAdapter.kt b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlacesAdapter.kt
new file mode 100644
index 0000000..7552292
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlacesAdapter.kt
@@ -0,0 +1,42 @@
+package com.mlsdev.mapsappsample.placesuggestions
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.mlsdev.mapsappsample.databinding.PlaceSuggestionsItemBinding
+import java.util.*
+
+class PlacesAdapter(
+ private val onItemClickListener: (place:String) -> Unit
+) : RecyclerView.Adapter() {
+ private var places: MutableList = ArrayList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val binding = PlaceSuggestionsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+
+ val viewModel = PlaceViewModel(onItemClickListener)
+ binding.viewModel = viewModel
+ return ViewHolder(binding.root, viewModel)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ holder.viewModel.placeFullText.set(places[position])
+ }
+
+ override fun getItemCount(): Int {
+ return places.size
+ }
+
+ inner class ViewHolder(itemView: View, val viewModel: PlaceViewModel) : RecyclerView.ViewHolder(itemView)
+
+ fun setData(places: MutableList) {
+ this.places = places
+ notifyDataSetChanged()
+ }
+
+ fun clearData() {
+ places.clear()
+ notifyDataSetChanged()
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlacesViewModel.kt b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlacesViewModel.kt
new file mode 100644
index 0000000..58337f1
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/placesuggestions/PlacesViewModel.kt
@@ -0,0 +1,55 @@
+package com.mlsdev.mapsappsample.placesuggestions
+
+import android.util.Log
+import androidx.databinding.BaseObservable
+import androidx.databinding.ObservableField
+import androidx.lifecycle.MutableLiveData
+import com.google.android.gms.common.api.GoogleApiClient
+import com.google.android.gms.common.api.ResultCallbacks
+import com.google.android.gms.common.api.Status
+import com.google.android.gms.location.places.AutocompleteFilter
+import com.google.android.gms.location.places.AutocompletePredictionBuffer
+import com.google.android.gms.location.places.Place
+import com.google.android.gms.location.places.Places
+import java.util.*
+
+class PlacesViewModel(private val googleApiClient: GoogleApiClient?) : BaseObservable() {
+ var isItemSelected: Boolean = false
+ val searchFieldValue: ObservableField = ObservableField()
+ var placesLiveData = MutableLiveData>()
+
+ fun searchPlaces(value: String) {
+ isItemSelected = false
+ val autocompleteFilter = AutocompleteFilter.Builder()
+ .setTypeFilter(Place.TYPE_STREET_ADDRESS)
+ .build()
+
+ if (googleApiClient == null)
+ return
+
+ Places.GeoDataApi.getAutocompletePredictions(googleApiClient, value, null, autocompleteFilter)
+ .setResultCallback(object : ResultCallbacks() {
+ override fun onSuccess(autocompletePredictions: AutocompletePredictionBuffer) {
+ val placesFullTextList = ArrayList(autocompletePredictions.count)
+ for (prediction in autocompletePredictions)
+ placesFullTextList.add(prediction.getFullText(null).toString())
+
+ autocompletePredictions.release()
+
+ placesLiveData.postValue(placesFullTextList)
+ }
+
+ override fun onFailure(status: Status) {
+ Log.e("PLACES_API", "Error: Places.GeoDataApi.getAutocompletePredictions()")
+ }
+ })
+ }
+
+
+
+ fun onPlaceSelected(placeFullText: String) {
+ isItemSelected = true
+ placesLiveData.postValue(emptyList())
+ searchFieldValue.set(placeFullText)
+ }
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/url/Constants.java b/app/src/main/java/com/mlsdev/mapsappsample/url/Constants.java
deleted file mode 100644
index 32e57ca..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/url/Constants.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.mlsdev.mapsappsample.url;
-
-public class Constants {
- public static final String BASE_URL = "https://maps.googleapis.com/maps/api/staticmap";
-
- // region Parameters
- public static final String CENTER = "center";
- public static final String SCALE = "scale";
- public static final String SIZE = "size";
- public static final String KEY = "key";
- public static final String ZOOM = "zoom";
- public static final String MAP_TYPE = "maptype";
- public static final String MARKERS = "markers";
- // endregion
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/url/Constants.kt b/app/src/main/java/com/mlsdev/mapsappsample/url/Constants.kt
new file mode 100644
index 0000000..c8a9d6a
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/url/Constants.kt
@@ -0,0 +1,15 @@
+package com.mlsdev.mapsappsample.url
+
+object Constants {
+ const val BASE_URL = "https://maps.googleapis.com/maps/api/staticmap"
+
+ // region Parameters
+ const val CENTER = "center"
+ const val SCALE = "scale"
+ const val SIZE = "size"
+ const val KEY = "key"
+ const val ZOOM = "zoom"
+ const val MAP_TYPE = "maptype"
+ const val MARKERS = "markers"
+ // endregion
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/url/UrlBuilder.java b/app/src/main/java/com/mlsdev/mapsappsample/url/UrlBuilder.java
deleted file mode 100644
index 0e2c075..0000000
--- a/app/src/main/java/com/mlsdev/mapsappsample/url/UrlBuilder.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.mlsdev.mapsappsample.url;
-
-import android.net.Uri;
-import android.support.annotation.NonNull;
-
-import java.util.Map;
-
-public class UrlBuilder {
-
- public static String buildUrl(@NonNull Map params) {
- Uri.Builder uriBuilder = Uri.parse(Constants.BASE_URL).buildUpon();
- for (Map.Entry entry : params.entrySet())
- uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
- return uriBuilder.build().toString();
- }
-
-}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/url/UrlBuilder.kt b/app/src/main/java/com/mlsdev/mapsappsample/url/UrlBuilder.kt
new file mode 100644
index 0000000..049e0a9
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/url/UrlBuilder.kt
@@ -0,0 +1,14 @@
+package com.mlsdev.mapsappsample.url
+
+import android.net.Uri
+
+object UrlBuilder {
+
+ fun buildUrl(params: Map): String {
+ val uriBuilder = Uri.parse(Constants.BASE_URL).buildUpon()
+ for ((key, value) in params)
+ uriBuilder.appendQueryParameter(key, value)
+ return uriBuilder.build().toString()
+ }
+
+}
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/utils/DrawableToBitmapDecoder.kt b/app/src/main/java/com/mlsdev/mapsappsample/utils/DrawableToBitmapDecoder.kt
new file mode 100644
index 0000000..16db544
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/utils/DrawableToBitmapDecoder.kt
@@ -0,0 +1,37 @@
+package com.mlsdev.mapsappsample.utils
+
+import android.annotation.TargetApi
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.VectorDrawable
+import android.os.Build
+import androidx.core.content.ContextCompat
+
+
+object DrawableToBitmapDecoder {
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ fun getBitmap(vectorDrawable: VectorDrawable): Bitmap {
+ val bitmap = Bitmap.createBitmap(vectorDrawable.intrinsicWidth,
+ vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
+ vectorDrawable.draw(canvas)
+ return bitmap
+ }
+
+ fun getBitmap(context: Context, drawableId: Int): Bitmap {
+ val drawable = ContextCompat.getDrawable(context, drawableId)
+ return if (drawable is BitmapDrawable) {
+ BitmapFactory.decodeResource(context.resources, drawableId)
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable is VectorDrawable) {
+ getBitmap((drawable as VectorDrawable?)!!)
+ } else {
+ throw IllegalArgumentException("unsupported drawable type")
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mlsdev/mapsappsample/utils/MapUtils.kt b/app/src/main/java/com/mlsdev/mapsappsample/utils/MapUtils.kt
new file mode 100644
index 0000000..36cd724
--- /dev/null
+++ b/app/src/main/java/com/mlsdev/mapsappsample/utils/MapUtils.kt
@@ -0,0 +1,37 @@
+package com.mlsdev.mapsappsample.utils
+
+import android.util.Log
+import com.google.android.gms.maps.model.LatLng
+import java.text.DecimalFormat
+import kotlin.math.*
+
+object MapUtils {
+
+ fun calculationByDistance(from: LatLng, to: LatLng): Double {
+ val radius = 6371// radius of earth in Km
+ val lat1 = from.latitude
+ val lat2 = to.latitude
+ val lon1 = from.longitude
+ val lon2 = to.longitude
+ val dLat = Math.toRadians(lat2 - lat1)
+ val dLon = Math.toRadians(lon2 - lon1)
+ val a = sin(dLat / 2) * sin(dLat / 2) + (cos(Math.toRadians(lat1))
+ * cos(Math.toRadians(lat2)) * sin(dLon / 2)
+ * sin(dLon / 2))
+ val c = 2 * asin(sqrt(a))
+ val valueResult = radius * c
+ val km = valueResult / 1
+ val newFormat = DecimalFormat("####")
+ val kmInDec = Integer.valueOf(newFormat.format(km))
+ val meter = valueResult % 1000
+ val meterInDec = Integer.valueOf(newFormat.format(meter))
+ Log.i("Radius Value", "" + valueResult + " KM " + kmInDec
+ + " Meter " + meterInDec)
+
+ return radius * c
+ }
+
+ fun calculationByDistanceInKm(from: LatLng, to: LatLng): Double =
+ (calculationByDistance(from, to) * 100.0).roundToInt() / 100.0
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_markers_cluster.xml b/app/src/main/res/drawable/bg_markers_cluster.xml
new file mode 100644
index 0000000..c40304e
--- /dev/null
+++ b/app/src/main/res/drawable/bg_markers_cluster.xml
@@ -0,0 +1,14 @@
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_map.xml b/app/src/main/res/drawable/ic_map.xml
new file mode 100644
index 0000000..b49149f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_map.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker.xml b/app/src/main/res/drawable/ic_marker.xml
new file mode 100644
index 0000000..6a3e8a3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker_healthcare_center.xml b/app/src/main/res/drawable/ic_marker_healthcare_center.xml
new file mode 100644
index 0000000..96586c4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker_healthcare_center.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker_healthcare_center_large.xml b/app/src/main/res/drawable/ic_marker_healthcare_center_large.xml
new file mode 100644
index 0000000..f1d0803
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker_healthcare_center_large.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker_laboratory.xml b/app/src/main/res/drawable/ic_marker_laboratory.xml
new file mode 100644
index 0000000..ce8c3b5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker_laboratory.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker_laboratory_large.xml b/app/src/main/res/drawable/ic_marker_laboratory_large.xml
new file mode 100644
index 0000000..24d7e41
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker_laboratory_large.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker_vaccination.xml b/app/src/main/res/drawable/ic_marker_vaccination.xml
new file mode 100644
index 0000000..fbb67f2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker_vaccination.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_marker_vaccination_large.xml b/app/src/main/res/drawable/ic_marker_vaccination_large.xml
new file mode 100644
index 0000000..e7e917e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_marker_vaccination_large.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_markers_cluster.xml b/app/src/main/res/drawable/ic_markers_cluster.xml
new file mode 100644
index 0000000..a72cf34
--- /dev/null
+++ b/app/src/main/res/drawable/ic_markers_cluster.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_pick_place.xml b/app/src/main/res/drawable/ic_pick_place.xml
new file mode 100644
index 0000000..cb60430
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pick_place.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_place_suggestion.xml b/app/src/main/res/drawable/ic_place_suggestion.xml
new file mode 100644
index 0000000..dfd2612
--- /dev/null
+++ b/app/src/main/res/drawable/ic_place_suggestion.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_primary_marker.xml b/app/src/main/res/drawable/ic_primary_marker.xml
new file mode 100644
index 0000000..c607491
--- /dev/null
+++ b/app/src/main/res/drawable/ic_primary_marker.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_static_image.xml b/app/src/main/res/drawable/ic_static_image.xml
new file mode 100644
index 0000000..8f654b9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_static_image.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 71ea31d..d9f6cdb 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -4,14 +4,9 @@
-
+ android:paddingTop="@dimen/activity_vertical_margin">
-
+
+
+
+
-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_marker_clustering.xml b/app/src/main/res/layout/activity_marker_clustering.xml
new file mode 100644
index 0000000..74aea53
--- /dev/null
+++ b/app/src/main/res/layout/activity_marker_clustering.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_place_picker.xml b/app/src/main/res/layout/activity_place_picker.xml
index 79c0cbc..1a77e51 100644
--- a/app/src/main/res/layout/activity_place_picker.xml
+++ b/app/src/main/res/layout/activity_place_picker.xml
@@ -9,7 +9,6 @@
+ android:paddingTop="@dimen/activity_vertical_margin">
-
-
@@ -39,23 +37,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
-
diff --git a/app/src/main/res/layout/activity_place_suggestions.xml b/app/src/main/res/layout/activity_place_suggestions.xml
index 16e57db..351c123 100644
--- a/app/src/main/res/layout/activity_place_suggestions.xml
+++ b/app/src/main/res/layout/activity_place_suggestions.xml
@@ -1,36 +1,94 @@
-
-
-
-
-
+
+
+
+
+
+
-
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_static_image_map.xml b/app/src/main/res/layout/activity_static_image_map.xml
index c29ffef..6f355f4 100644
--- a/app/src/main/res/layout/activity_static_image_map.xml
+++ b/app/src/main/res/layout/activity_static_image_map.xml
@@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
-
-
-
-
-
-
-
-
-
-
@@ -111,21 +111,27 @@
-
+
+
+
-
diff --git a/app/src/main/res/layout/layout_markers_cluster.xml b/app/src/main/res/layout/layout_markers_cluster.xml
new file mode 100644
index 0000000..8e37ec6
--- /dev/null
+++ b/app/src/main/res/layout/layout_markers_cluster.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/place_suggestions_item.xml b/app/src/main/res/layout/place_suggestions_item.xml
new file mode 100644
index 0000000..e1391c8
--- /dev/null
+++ b/app/src/main/res/layout/place_suggestions_item.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_cancel.png b/app/src/main/res/mipmap-xxhdpi/ic_cancel.png
new file mode 100644
index 0000000..55780b6
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_cancel.png differ
diff --git a/app/src/main/res/raw/radar_search.json b/app/src/main/res/raw/radar_search.json
new file mode 100644
index 0000000..84acb66
--- /dev/null
+++ b/app/src/main/res/raw/radar_search.json
@@ -0,0 +1,202 @@
+[
+{ "lat" : 51.5145160, "lng" : -0.1270060 },
+{ "lat" : 51.5064490, "lng" : -0.1244260, "title" : "Corinthia Hotel London", "snippet": "Whitehall Pl"},
+{ "lat" : 51.5097080, "lng" : -0.1200450, "title" : "Savoy Place", "snippet" : "Covent Garden"},
+{ "lat" : 51.5090680, "lng" : -0.1421420, "title" : "Albemarle St", "snippet": "Mayfair"},
+{ "lat" : 51.4976080, "lng" : -0.1456320, "title" : " Victoria Square", "snippet": " Belgravia" },
+{ "lat" : 51.5046150, "lng" : -0.1473780},
+{ "lat" : 51.5077540, "lng" : -0.1378760, "title" : "Jermyn Street", "snippet": "St. James's" },
+{ "lat" : 51.5074250, "lng" : -0.1323230 , "title" : "Pall Mall", "snippet": "Westminster"},
+{ "lat" : 51.5070030, "lng" : -0.125560 },
+{ "lat" : 51.5061590, "lng" : -0.140280 },
+{ "lat" : 51.5047420, "lng" : -0.1470490 },
+{ "lat" : 51.5126760, "lng" : -0.1189760 },
+{ "lat" : 51.5108480, "lng" : -0.1208480 },
+{ "lat" : 51.5099460, "lng" : -0.1300150 },
+{ "lat" : 51.5076580, "lng" : -0.1424490 },
+{ "lat" : 51.5097160, "lng" : -0.1555350 },
+{ "lat" : 51.5215190, "lng" : -0.1621160 },
+{ "lat" : 51.5177960, "lng" : -0.1438760 },
+{ "lat" : 51.5071840, "lng" : -0.1415940 },
+{ "lat" : 51.5008150, "lng" : -0.1520910 },
+{ "lat" : 51.5179170, "lng" : -0.142740 },
+{ "lat" : 51.50360, "lng" : -0.14980 },
+{ "lat" : 51.512620, "lng" : -0.1476950 },
+{ "lat" : 51.5051890, "lng" : -0.08813600000000001 },
+{ "lat" : 51.4969390, "lng" : -0.1594880 },
+{ "lat" : 51.506020, "lng" : -0.1241340 },
+{ "lat" : 51.5143270, "lng" : -0.1318940 },
+{ "lat" : 51.5070480, "lng" : -0.1521930 },
+{ "lat" : 51.5101640, "lng" : -0.1495920 },
+{ "lat" : 51.5144240, "lng" : -0.1392980 },
+{ "lat" : 51.4816380, "lng" : -0.1489180 },
+{ "lat" : 51.519340, "lng" : -0.1209080 },
+{ "lat" : 51.4982420, "lng" : -0.1435160 },
+{ "lat" : 51.5104310, "lng" : -0.1267850 },
+{ "lat" : 51.504340, "lng" : -0.149940 },
+{ "lat" : 51.5174490, "lng" : -0.1370170 },
+{ "lat" : 51.524370, "lng" : -0.1281460 },
+{ "lat" : 51.5117770, "lng" : -0.1192630 },
+{ "lat" : 51.5026220, "lng" : -0.1527130 },
+{ "lat" : 51.500210, "lng" : -0.1798050 },
+{ "lat" : 51.5293450, "lng" : -0.1260930 },
+{ "lat" : 51.514070, "lng" : -0.0854980 },
+{ "lat" : 51.5154290, "lng" : -0.1571420 },
+{ "lat" : 51.5158980, "lng" : -0.1202010 },
+{ "lat" : 51.5081370, "lng" : -0.1438340 },
+{ "lat" : 51.4990650, "lng" : -0.1343110 },
+{ "lat" : 51.5059290, "lng" : -0.1491020 },
+{ "lat" : 51.5017470, "lng" : -0.1848540 },
+{ "lat" : 51.510820, "lng" : -0.1511460 },
+{ "lat" : 51.5128620, "lng" : -0.192130 },
+{ "lat" : 51.49850, "lng" : -0.1583950 },
+{ "lat" : 51.5094440, "lng" : -0.1362880 },
+{ "lat" : 51.5239250, "lng" : -0.1249180 },
+{ "lat" : 51.4930180, "lng" : -0.159680 },
+{ "lat" : 51.5055380, "lng" : -0.1396880 },
+{ "lat" : 51.4892760, "lng" : -0.180180 },
+{ "lat" : 51.4999860, "lng" : -0.161490 },
+{ "lat" : 51.5183870, "lng" : -0.1350570 },
+{ "lat" : 51.5025980, "lng" : -0.1883030 },
+{ "lat" : 51.4702570, "lng" : -0.1776650 },
+{ "lat" : 51.5184940, "lng" : -0.1452920 },
+{ "lat" : 51.4943450, "lng" : -0.1145180 },
+{ "lat" : 51.5174860, "lng" : -0.1307490 },
+{ "lat" : 51.47570, "lng" : -0.1819760 },
+{ "lat" : 51.4917980, "lng" : -0.161740 },
+{ "lat" : 51.5081480, "lng" : -0.1653260 },
+{ "lat" : 51.5241310, "lng" : -0.18460 },
+{ "lat" : 51.5255790, "lng" : -0.0828410 },
+{ "lat" : 51.4944410, "lng" : -0.1360670 },
+{ "lat" : 51.4924780, "lng" : -0.1483010 },
+{ "lat" : 51.5101220, "lng" : -0.1967860 },
+{ "lat" : 51.4947680, "lng" : -0.1186810 },
+{ "lat" : 51.5108440, "lng" : -0.131580 },
+{ "lat" : 51.4906890, "lng" : -0.1386160 },
+{ "lat" : 51.4991350, "lng" : -0.1125320 },
+{ "lat" : 51.5113950, "lng" : -0.1427780 },
+{ "lat" : 51.4905960, "lng" : -0.1388970 },
+{ "lat" : 51.4908430, "lng" : -0.1440980 },
+{ "lat" : 51.4900210, "lng" : -0.1376870 },
+{ "lat" : 51.5102170, "lng" : -0.1315040 },
+{ "lat" : 51.4903170, "lng" : -0.1377590 },
+{ "lat" : 51.5101040, "lng" : -0.1322540 },
+{ "lat" : 51.5156830, "lng" : -0.1240560 },
+{ "lat" : 51.5116380, "lng" : -0.1384670 },
+{ "lat" : 51.4973190, "lng" : -0.156080 },
+{ "lat" : 51.5180390, "lng" : -0.1497690 },
+{ "lat" : 51.4930840, "lng" : -0.1443660 },
+{ "lat" : 51.498970, "lng" : -0.1062350 },
+{ "lat" : 51.5113380, "lng" : -0.1300990 },
+{ "lat" : 51.4920160, "lng" : -0.1419380 },
+{ "lat" : 51.507070, "lng" : -0.1049530 },
+{ "lat" : 51.5059030, "lng" : -0.1403950 },
+{ "lat" : 51.5160770, "lng" : -0.1353810 },
+{ "lat" : 51.494140, "lng" : -0.1411990 },
+{ "lat" : 51.5225950, "lng" : -0.1253190 },
+{ "lat" : 51.4957540, "lng" : -0.1476890 },
+{ "lat" : 51.5052860, "lng" : -0.150260 },
+{ "lat" : 51.4966970, "lng" : -0.1122920 },
+{ "lat" : 51.5201680, "lng" : -0.1256960 },
+{ "lat" : 51.4929010, "lng" : -0.1572410 },
+{ "lat" : 51.5019440, "lng" : -0.1562350 },
+{ "lat" : 51.489530, "lng" : -0.1366590 },
+{ "lat" : 51.5134410, "lng" : -0.1508490 },
+{ "lat" : 51.5025440, "lng" : -0.118110 },
+{ "lat" : 51.491640, "lng" : -0.1415830 },
+{ "lat" : 51.5115110, "lng" : -0.1183470 },
+{ "lat" : 51.4909510, "lng" : -0.1438210 },
+{ "lat" : 51.5071040, "lng" : -0.1461620 },
+{ "lat" : 51.5186970, "lng" : -0.1355270 },
+{ "lat" : 51.5178830, "lng" : -0.1185430 },
+{ "lat" : 51.492470, "lng" : -0.1455250 },
+{ "lat" : 51.5015740, "lng" : -0.1624430 },
+{ "lat" : 51.5135820, "lng" : -0.1086290 },
+{ "lat" : 51.4920260, "lng" : -0.1418470 },
+{ "lat" : 51.4907580, "lng" : -0.1445780 },
+{ "lat" : 51.4913140, "lng" : -0.144820 },
+{ "lat" : 51.490830, "lng" : -0.1443390 },
+{ "lat" : 51.5139170, "lng" : -0.122090 },
+{ "lat" : 51.4922580, "lng" : -0.1418670 },
+{ "lat" : 51.5160140, "lng" : -0.1578750 },
+{ "lat" : 51.5109460, "lng" : -0.076930 },
+{ "lat" : 51.4930270, "lng" : -0.142960 },
+{ "lat" : 51.49990, "lng" : -0.1781820 },
+{ "lat" : 51.5035550, "lng" : -0.1113130 },
+{ "lat" : 51.4903060, "lng" : -0.1403020 },
+{ "lat" : 51.4936240, "lng" : -0.1500960 },
+{ "lat" : 51.4919830, "lng" : -0.1414270 },
+{ "lat" : 51.5050970, "lng" : -0.104710 },
+{ "lat" : 51.4950920, "lng" : -0.1838080 },
+{ "lat" : 51.5259280, "lng" : -0.1358780 },
+{ "lat" : 51.5057060, "lng" : -0.1221970 },
+{ "lat" : 51.4952410, "lng" : -0.1817070 },
+{ "lat" : 51.4940420, "lng" : -0.1492270 },
+{ "lat" : 51.490370, "lng" : -0.1458720 },
+{ "lat" : 51.5077260, "lng" : -0.1471730 },
+{ "lat" : 51.4651970, "lng" : -0.114940 },
+{ "lat" : 51.5138860, "lng" : -0.1012070 },
+{ "lat" : 51.518720, "lng" : -0.153870 },
+{ "lat" : 51.4913010, "lng" : -0.1428370 },
+{ "lat" : 51.5151350, "lng" : -0.1615710 },
+{ "lat" : 51.5188840, "lng" : -0.1318310 },
+{ "lat" : 51.5020890, "lng" : -0.186790 },
+{ "lat" : 51.4907170, "lng" : -0.1386760 },
+{ "lat" : 51.5182760, "lng" : -0.1582390 },
+{ "lat" : 51.5195190, "lng" : -0.1430740 },
+{ "lat" : 51.5241420, "lng" : -0.1378130 },
+{ "lat" : 51.5077230, "lng" : -0.1279620 },
+{ "lat" : 51.4938750, "lng" : -0.1497910 },
+{ "lat" : 51.5237550, "lng" : -0.1403980 },
+{ "lat" : 51.4933780, "lng" : -0.1500050 },
+{ "lat" : 51.4901580, "lng" : -0.1387650 },
+{ "lat" : 51.5159630, "lng" : -0.1718890 },
+{ "lat" : 51.4908250, "lng" : -0.1450190 },
+{ "lat" : 51.5264680, "lng" : -0.1353710 },
+{ "lat" : 51.5173210, "lng" : -0.156650 },
+{ "lat" : 51.5140620, "lng" : -0.1469230 },
+{ "lat" : 51.5028270, "lng" : -0.0720070 },
+{ "lat" : 51.5206650, "lng" : -0.1003340 },
+{ "lat" : 51.502060, "lng" : -0.1599840 },
+{ "lat" : 51.5277390, "lng" : -0.135320 },
+{ "lat" : 51.5219420, "lng" : -0.1321670 },
+{ "lat" : 51.5152990, "lng" : -0.1600470 },
+{ "lat" : 51.4907870, "lng" : -0.1388240 },
+{ "lat" : 51.4945090, "lng" : -0.174880 },
+{ "lat" : 51.5219720, "lng" : -0.1322210 },
+{ "lat" : 51.4900030, "lng" : -0.0977280 },
+{ "lat" : 51.5222270, "lng" : -0.1425630 },
+{ "lat" : 51.4977620, "lng" : -0.08125599999999999 },
+{ "lat" : 51.5081250, "lng" : -0.07108100000000001 },
+{ "lat" : 51.5179610, "lng" : -0.1529610 },
+{ "lat" : 51.5129390, "lng" : -0.1577410 },
+{ "lat" : 51.4935420, "lng" : -0.1594010 },
+{ "lat" : 51.5157770, "lng" : -0.173090 },
+{ "lat" : 51.4948150, "lng" : -0.1778260 },
+{ "lat" : 51.4964460, "lng" : -0.1557010 },
+{ "lat" : 51.4916430, "lng" : -0.1006060 },
+{ "lat" : 51.5122920, "lng" : -0.1750390 },
+{ "lat" : 51.5202170, "lng" : -0.1025320 },
+{ "lat" : 51.5076930, "lng" : -0.1413620 },
+{ "lat" : 51.491520, "lng" : -0.1426860 },
+{ "lat" : 51.5138990, "lng" : -0.1787940 },
+{ "lat" : 51.5221330, "lng" : -0.1325740 },
+{ "lat" : 51.5135120, "lng" : -0.1631850 },
+{ "lat" : 51.5067690, "lng" : -0.0740210 },
+{ "lat" : 51.5201120, "lng" : -0.1238560 },
+{ "lat" : 51.4925250, "lng" : -0.1451160 },
+{ "lat" : 51.5162150, "lng" : -0.1733980 },
+{ "lat" : 51.5228320, "lng" : -0.155350 },
+{ "lat" : 51.5212420, "lng" : -0.1558640 },
+{ "lat" : 51.5284530, "lng" : -0.1226970 },
+{ "lat" : 51.5223030, "lng" : -0.1325830 },
+{ "lat" : 51.5197430, "lng" : -0.1431740 },
+{ "lat" : 51.5216440, "lng" : -0.1318630 },
+{ "lat" : 51.5172640, "lng" : -0.08090 },
+{ "lat" : 51.474050, "lng" : -0.0895920 },
+{ "lat" : 51.5181490, "lng" : -0.1581730 },
+{ "lat" : 51.5159250, "lng" : -0.1600310 },
+{ "lat" : 51.5243350, "lng" : -0.1165120 },
+{ "lat" : 51.4907560, "lng" : -0.1434930 },
+{ "lat" : 51.4900980, "lng" : -0.1373290 }
+]
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3ab3e9c..8a7e673 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,4 +3,5 @@
#3F51B5
#303F9F
#FF4081
+ #009688
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 850c449..6ea8d2b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -19,6 +19,14 @@
get image map
map type
place suggestions
+ Search
+ Marker Clustering
+ Markers Clustering
+
+ %1$s \nDistance to your current location is %2$s km
+ Marker info
+ Close
+ MLSDev maps sandbox
- roadmap
diff --git a/app/src/test/java/com/mlsdev/mapsappsample/ExampleUnitTest.java b/app/src/test/java/com/mlsdev/mapsappsample/ExampleUnitTest.java
deleted file mode 100644
index 6f170de..0000000
--- a/app/src/test/java/com/mlsdev/mapsappsample/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.mlsdev.mapsappsample;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 607a23c..dc354c3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,13 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.3.41'
repositories {
+ google()
jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.2'
- classpath 'me.tatarka:gradle-retrolambda:3.2.3'
-
+ classpath 'com.android.tools.build:gradle:3.4.1'
+ classpath 'com.google.gms:google-services:4.3.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -15,7 +18,10 @@ buildscript {
allprojects {
repositories {
+ google()
jcenter()
+ mavenCentral()
+ maven { url "https://jitpack.io" }
}
}
diff --git a/gradle.properties b/gradle.properties
index aac7c9b..706bcd1 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,3 +15,6 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+
+android.useAndroidX=true
+android.enableJetifier=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 04e285f..a4b55d0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Dec 28 10:00:20 PST 2015
+#Tue Jul 09 14:39:48 EEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/screenshots/1.png b/screenshots/1.png
index b33bc9a..045cdaf 100644
Binary files a/screenshots/1.png and b/screenshots/1.png differ
diff --git a/screenshots/10.png b/screenshots/10.png
new file mode 100644
index 0000000..0704ab6
Binary files /dev/null and b/screenshots/10.png differ
diff --git a/screenshots/11.png b/screenshots/11.png
new file mode 100644
index 0000000..6077181
Binary files /dev/null and b/screenshots/11.png differ
diff --git a/screenshots/12.png b/screenshots/12.png
new file mode 100644
index 0000000..65b9f42
Binary files /dev/null and b/screenshots/12.png differ
diff --git a/screenshots/13.png b/screenshots/13.png
new file mode 100644
index 0000000..925674d
Binary files /dev/null and b/screenshots/13.png differ
diff --git a/screenshots/14.png b/screenshots/14.png
new file mode 100644
index 0000000..bd83225
Binary files /dev/null and b/screenshots/14.png differ
diff --git a/screenshots/15.png b/screenshots/15.png
new file mode 100644
index 0000000..5abb14d
Binary files /dev/null and b/screenshots/15.png differ
diff --git a/screenshots/16.png b/screenshots/16.png
new file mode 100644
index 0000000..7168e55
Binary files /dev/null and b/screenshots/16.png differ
diff --git a/screenshots/17.png b/screenshots/17.png
new file mode 100644
index 0000000..18f5f31
Binary files /dev/null and b/screenshots/17.png differ