From d4ee4a849afefd9f9d62b8fe10c9f6840ba6069e Mon Sep 17 00:00:00 2001 From: Johan von Forstner Date: Thu, 19 Mar 2020 22:43:56 +0100 Subject: [PATCH] implement my location button --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 5 - .../main/java/com/johan/evmap/MapsActivity.kt | 69 ++++++++++- .../com/johan/evmap/ui/BindingAdapters.kt | 20 +++- .../johan/evmap/ui/HideOnScrollFabBehavior.kt | 113 ++++++++++++++++++ app/src/main/res/drawable/ic_location.xml | 9 ++ app/src/main/res/layout/activity_maps.xml | 18 +++ app/src/main/res/layout/detail_view.xml | 4 +- 8 files changed, 224 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/johan/evmap/ui/HideOnScrollFabBehavior.kt create mode 100644 app/src/main/res/drawable/ic_location.xml diff --git a/app/build.gradle b/app/build.gradle index 3d9724ca..52b93c1d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'com.google.maps.android:android-maps-utils:0.5' implementation 'com.github.johan12345:CustomBottomSheetBehavior:-SNAPSHOT' implementation 'com.google.android.gms:play-services-maps:17.0.0' + implementation 'com.google.android.gms:play-services-location:17.0.0' implementation 'com.squareup.retrofit2:retrofit:2.7.2' implementation 'com.squareup.retrofit2:converter-moshi:2.7.2' implementation 'com.squareup.moshi:moshi-kotlin:1.9.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bfd35bb2..9117872e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,11 +2,6 @@ - = emptyList() private var markers: Map = emptyMap() private var clusterMarkers: List = emptyList() @@ -38,6 +45,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_maps) + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) @@ -78,16 +86,24 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback { } }) + + binding.fabLocate.setOnClickListener { + if (!hasLocationPermission()) { + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + REQUEST_LOCATION_PERMISSION + ) + } else { + enableLocation(true) + } + } + } override fun onMapReady(googleMap: GoogleMap) { map = googleMap - - // Add a marker in Sydney and move the camera - val sydney = LatLng(54.0, 9.0) - map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 11f)) - map.setOnCameraIdleListener { loadChargepoints() } @@ -108,8 +124,34 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback { map.setOnMapClickListener { binding.charger = null } + + if (hasLocationPermission()) { + enableLocation(false) + } } + private fun enableLocation(animate: Boolean) { + map.isMyLocationEnabled = true + binding.myLocationEnabled = true + map.uiSettings.isMyLocationButtonEnabled = false + fusedLocationClient.lastLocation.addOnSuccessListener { location -> + if (location != null) { + val latLng = LatLng(location.latitude, location.longitude) + if (animate) { + val camUpdate = CameraUpdateFactory.newLatLng(latLng) + map.animateCamera(camUpdate) + } else { + val camUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 13f) + map.moveCamera(camUpdate) + } + } + } + } + + private fun hasLocationPermission() = + ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == + PackageManager.PERMISSION_GRANTED + private fun loadChargepoints() { val bounds = map.projection.visibleRegion.latLngBounds api.getChargepoints( @@ -197,4 +239,19 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback { ) } } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + when (requestCode) { + REQUEST_LOCATION_PERMISSION -> { + if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + enableLocation(true) + } + } + else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + } } diff --git a/app/src/main/java/com/johan/evmap/ui/BindingAdapters.kt b/app/src/main/java/com/johan/evmap/ui/BindingAdapters.kt index fc4e928c..a518a96f 100644 --- a/app/src/main/java/com/johan/evmap/ui/BindingAdapters.kt +++ b/app/src/main/java/com/johan/evmap/ui/BindingAdapters.kt @@ -1,14 +1,30 @@ package com.johan.evmap.ui +import android.R +import android.content.res.ColorStateList +import android.util.TypedValue import android.view.View import androidx.databinding.BindingAdapter +import com.google.android.material.floatingactionbutton.FloatingActionButton -@BindingAdapter("app:goneUnless") + +@BindingAdapter("goneUnless") fun goneUnless(view: View, visible: Boolean) { view.visibility = if (visible) View.VISIBLE else View.GONE } -@BindingAdapter("app:invisibleUnless") +@BindingAdapter("invisibleUnless") fun invisibleUnless(view: View, visible: Boolean) { view.visibility = if (visible) View.VISIBLE else View.INVISIBLE +} + +@BindingAdapter("isFabActive") +fun isFabActive(view: FloatingActionButton, isColored: Boolean) { + val color = TypedValue() + if (isColored) { + view.context.theme.resolveAttribute(R.attr.colorAccent, color, true) + } else { + view.context.theme.resolveAttribute(R.attr.colorControlNormal, color, true) + } + view.imageTintList = ColorStateList.valueOf(color.data) } \ No newline at end of file diff --git a/app/src/main/java/com/johan/evmap/ui/HideOnScrollFabBehavior.kt b/app/src/main/java/com/johan/evmap/ui/HideOnScrollFabBehavior.kt new file mode 100644 index 00000000..31fe7e40 --- /dev/null +++ b/app/src/main/java/com/johan/evmap/ui/HideOnScrollFabBehavior.kt @@ -0,0 +1,113 @@ +package com.johan.evmap.ui + +import android.content.Context +import android.util.AttributeSet +import android.util.Log +import android.view.View +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.ViewCompat +import androidx.core.widget.NestedScrollView +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike + + +class HideOnScrollFabBehavior(context: Context, attrs: AttributeSet) : + FloatingActionButton.Behavior(context, attrs) { + + override fun onStartNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: FloatingActionButton, + directTargetChild: View, + target: View, + axes: Int, + type: Int + ): Boolean { + Log.d("debug", "onStartNestedScroll") + return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll( + coordinatorLayout, + child, + directTargetChild, + target, + axes, + type + ) + } + + override fun layoutDependsOn( + parent: CoordinatorLayout, + child: FloatingActionButton, + dependency: View + ): Boolean { + if (dependency is NestedScrollView) { + try { + val behavior = BottomSheetBehaviorGoogleMapsLike.from(dependency) + behavior.addBottomSheetCallback(object : + BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() { + override fun onSlide(bottomSheet: View, slideOffset: Float) { + + } + + override fun onStateChanged(bottomSheet: View, newState: Int) { + onDependentViewChanged(parent, child, dependency) + } + }) + return true + } catch (e: IllegalArgumentException) { + } + } + return false + } + + override fun onDependentViewChanged( + parent: CoordinatorLayout, + child: FloatingActionButton, + dependency: View + ): Boolean { + val behavior = BottomSheetBehaviorGoogleMapsLike.from(dependency) + Log.d("debug", "state: ${behavior.state}") + when (behavior.state) { + BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING -> { + + } + BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN -> { + child.show() + } + else -> { + child.hide() + } + } + return false + } + + override fun onNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: FloatingActionButton, + target: View, + dxConsumed: Int, + dyConsumed: Int, + dxUnconsumed: Int, + dyUnconsumed: Int, + type: Int, + consumed: IntArray + ) { + Log.d("debug", "onNestedScroll $dyConsumed") + super.onNestedScroll( + coordinatorLayout, + child, + target, + dxConsumed, + dyConsumed, + dxUnconsumed, + dyUnconsumed, + type, + consumed + ) + if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) { + // User scrolled down and the FAB is currently visible -> hide the FAB + child.hide(); + } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) { + // User scrolled up and the FAB is currently not visible -> show the FAB + child.show(); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 00000000..2291bc1f --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_maps.xml b/app/src/main/res/layout/activity_maps.xml index 917bca61..01ddb475 100644 --- a/app/src/main/res/layout/activity_maps.xml +++ b/app/src/main/res/layout/activity_maps.xml @@ -9,6 +9,10 @@ + + + +