diff --git a/app/build.gradle b/app/build.gradle
index b35c4c1d..41869f96 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -14,11 +14,11 @@ apply plugin: "androidx.navigation.safeargs.kotlin"
apply plugin: 'com.google.firebase.crashlytics'
android {
- compileSdkVersion 30
+ compileSdkVersion 31
defaultConfig {
applicationId "gov.wa.wsdot.android.wsdot"
minSdkVersion 21
- targetSdkVersion 30
+ targetSdkVersion 31
versionCode 21060703
versionName "6.7.3"
vectorDrawables.useSupportLibrary = true
@@ -87,37 +87,41 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'com.google.android.material:material:1.1.0'
- implementation 'androidx.activity:activity-ktx:1.1.0'
- implementation 'androidx.fragment:fragment-ktx:1.2.5'
- implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.activity:activity-ktx:1.4.0'
+ implementation 'androidx.fragment:fragment-ktx:1.4.0'
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
+
+ // Updating to 2.4.0 causes error. Duplicate classes found in modules.
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
- implementation 'com.google.android.gms:play-services-ads:20.2.0'
- implementation 'com.google.android.gms:play-services-location:17.1.0'
- implementation 'com.google.android.gms:play-services-maps:17.0.0'
+ implementation 'com.google.android.gms:play-services-ads:20.5.0'
+ implementation 'com.google.android.gms:play-services-location:19.0.1'
+ implementation 'com.google.android.gms:play-services-maps:18.0.2'
- implementation 'com.google.firebase:firebase-core:18.0.2'
- implementation 'com.google.firebase:firebase-analytics:18.0.2'
- implementation 'com.google.firebase:firebase-messaging:21.0.1'
- implementation 'com.google.firebase:firebase-crashlytics-ktx:17.3.1'
+ implementation 'com.google.firebase:firebase-core:20.0.2'
+ implementation 'com.google.firebase:firebase-analytics:20.0.2'
+ implementation 'com.google.firebase:firebase-messaging:23.0.0'
+ implementation 'com.google.firebase:firebase-crashlytics-ktx:18.2.6'
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'androidx.core:core-ktx:1.3.2'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.preference:preference-ktx:1.1.1'
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
- kapt "androidx.lifecycle:lifecycle-common-java8:2.2.0"
+ kapt "androidx.lifecycle:lifecycle-common-java8:2.4.0"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
- implementation "androidx.room:room-runtime:2.2.6"
- kapt "androidx.room:room-compiler:2.2.6"
- annotationProcessor 'androidx.room:room-compiler:2.2.6'
+ implementation "androidx.room:room-runtime:2.4.0"
+
+ // Updating to 2.4.1 causes app:kaptDebugKotlin error.
+ kapt "androidx.room:room-compiler:2.3.0"
+ annotationProcessor 'androidx.room:room-compiler:2.4.0'
// drag and drop list
implementation 'com.ernestoyaquello.dragdropswiperecyclerview:drag-drop-swipe-recyclerview:0.5.1'
@@ -132,7 +136,7 @@ dependencies {
kapt "com.android.databinding:compiler:3.1.4"
// Navigation - https://developer.android.com/jetpack/androidx/releases/navigation
- def nav_version = "2.3.3"
+ def nav_version = "2.3.5"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
@@ -153,6 +157,8 @@ dependencies {
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
+
+ // Updating to 2.28.3 causes app:kaptDebugKotlin error.
implementation "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
@@ -167,12 +173,12 @@ dependencies {
// Required -- JUnit 4 framework
testImplementation 'junit:junit:4.13.1'
// Optional -- Robolectric environment
- testImplementation 'androidx.test:core:1.3.0'
- androidTestImplementation 'androidx.test:core:1.3.0'
- testImplementation "androidx.room:room-testing:2.2.6"
+ testImplementation 'androidx.test:core:1.4.0'
+ androidTestImplementation 'androidx.test:core:1.4.0'
+ testImplementation "androidx.room:room-testing:2.4.0"
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
- androidTestImplementation 'androidx.test:runner:1.3.0'
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ androidTestImplementation 'androidx.test:runner:1.4.0'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e7868335..fce794db 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
@@ -51,6 +52,7 @@
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/repository/FerriesRepository.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/repository/FerriesRepository.kt
index fb9afd4f..ef69e5f2 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/repository/FerriesRepository.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/repository/FerriesRepository.kt
@@ -61,7 +61,7 @@ class FerriesRepository @Inject constructor(
if (data != null && data.isNotEmpty()) {
- if (TimeUtils.isOverXMinOld(data[0].localCacheDate, x = 15)) {
+ if (TimeUtils.isOverXMinOld(data[0].localCacheDate, x = 5)) {
update = true
}
} else {
@@ -94,7 +94,7 @@ class FerriesRepository @Inject constructor(
if (data != null) {
- if (TimeUtils.isOverXMinOld(data.localCacheDate, x = 15)) {
+ if (TimeUtils.isOverXMinOld(data.localCacheDate, x = 5)) {
update = true
}
} else {
@@ -267,7 +267,7 @@ class FerriesRepository @Inject constructor(
if (data != null && data.isNotEmpty()) {
- if (TimeUtils.isOverXMinOld(data[0].localCacheDate, x = 15)) {
+ if (TimeUtils.isOverXMinOld(data[0].localCacheDate, x = 5)) {
update = true
}
} else {
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/service/MyFirebaseMessagingService.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/service/MyFirebaseMessagingService.kt
index 54ee5509..afbded90 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/service/MyFirebaseMessagingService.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/service/MyFirebaseMessagingService.kt
@@ -4,7 +4,9 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
+import android.os.Build
import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.preference.PreferenceManager
import com.google.firebase.messaging.FirebaseMessagingService
@@ -71,6 +73,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
}
}
+ @RequiresApi(Build.VERSION_CODES.M)
private fun getNotificationIntent(data: MutableMap): PendingIntent {
val type = data["type"]
@@ -120,7 +123,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
}
}
return PendingIntent.getActivity(this, alertId?.toInt() ?: 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT)
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
/**
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/MainActivity.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/MainActivity.kt
index 642b2481..2aa41a0f 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/MainActivity.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/MainActivity.kt
@@ -57,7 +57,7 @@ class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemS
private lateinit var firebaseAnalytics: FirebaseAnalytics
- private lateinit var adView: AdManagerAdView
+ private var adView: AdManagerAdView? = null
private val adSize: AdSize
get() {
@@ -201,12 +201,12 @@ class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemS
adView = AdManagerAdView(this)
ad_banner_box.addView(adView)
- adView.setAdSizes(adSize)
- adView.adUnitId = ApiKeys.UNIT_ID
+ adView?.setAdSizes(adSize)
+ adView?.adUnitId = ApiKeys.UNIT_ID
}
- override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
addMenuBadgeIfNeeded()
return super.onCreateOptionsMenu(menu)
}
@@ -592,9 +592,9 @@ class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemS
* Initialize and display ads.
* WARNING: don't call in onCreate
*/
- fun enableAds(targets: Map) {
+ fun enableAds(targets: Map) {
- ad_banner_box.visibility = VISIBLE
+ ad_banner_box?.visibility = VISIBLE
//val testDeviceIds = Arrays.asList("2531EB5FD75758B5E8BDD4669A870BF7")
//val configuration = RequestConfiguration.Builder()
@@ -604,15 +604,15 @@ class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemS
//.build()
//MobileAds.setRequestConfiguration(configuration)
- adView.pause()
- adView.adListener = null
- adView.adListener = object : AdListener() {
+ adView?.pause()
+ adView?.adListener = null
+ adView?.adListener = object : AdListener() {
override fun onAdLoaded() {
super.onAdLoaded()
// report ad ID to crashlytics
- val info = adView.responseInfo
+ val info = adView?.responseInfo
var adResponseId = "null"
if (info != null){
Log.e("Ads", info.toString())
@@ -632,7 +632,7 @@ class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemS
}
// Start loading the ad in the background.
- adView.loadAd(adRequest.build())
+ adView?.loadAd(adRequest.build())
}
@@ -641,8 +641,8 @@ class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemS
* WARNING: don't call in onCreate
*/
fun disableAds() {
- ad_banner_box.visibility = GONE
- adView.pause()
+ ad_banner_box?.visibility = GONE
+ adView?.pause()
}
fun setScreenName(screenName: String) {
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/about/AboutFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/about/AboutFragment.kt
index 77d13da8..3e966be7 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/about/AboutFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/about/AboutFragment.kt
@@ -52,11 +52,6 @@ class AboutFragment: DaggerFragment(), Injectable {
startActivity(browserIntent)
}
- binding.heroButton.setOnClickListener {
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.wsdot.wa.gov/travel/highways-bridges/hov/report-violator"))
- startActivity(browserIntent)
- }
-
binding.appBugReportButton.setOnClickListener {
val emailIntent = Intent(Intent.ACTION_SEND)
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf("webfeedback@wsdot.wa.gov"))
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/amtrakcascades/AmtrakCascadesFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/amtrakcascades/AmtrakCascadesFragment.kt
index aaecce0e..becca378 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/amtrakcascades/AmtrakCascadesFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/amtrakcascades/AmtrakCascadesFragment.kt
@@ -2,12 +2,19 @@ package gov.wa.wsdot.android.wsdot.ui.amtrakcascades
import android.Manifest
import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.pm.PackageManager
import android.location.Location
+import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.*
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
@@ -23,8 +30,6 @@ import gov.wa.wsdot.android.wsdot.ui.common.callback.TapCallback
import gov.wa.wsdot.android.wsdot.ui.common.viewmodel.SharedDateViewModel
import gov.wa.wsdot.android.wsdot.util.autoCleared
import permissions.dispatcher.NeedsPermission
-import permissions.dispatcher.OnShowRationale
-import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
import java.util.*
import javax.inject.Inject
@@ -51,6 +56,29 @@ class AmtrakCascadesFragment : DaggerFragment(), Injectable {
private lateinit var fusedLocationClient: FusedLocationProviderClient
+ @RequiresApi(Build.VERSION_CODES.N)
+ val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ when {
+ permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
+ myLocationFineWithPermissionCheck()
+ println("Precise location access granted.")
+
+ }
+ permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
+ myLocationCoarseWithPermissionCheck()
+ println("Coarse location access granted.")
+
+ }
+ else -> {
+ println("No location access granted.")
+ }
+ }
+ }
+
+
+
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// analytics
@@ -67,8 +95,10 @@ class AmtrakCascadesFragment : DaggerFragment(), Injectable {
savedInstanceState: Bundle?
): View? {
- val adTargets = mapOf("wsdotapp" to "other")
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to "other")
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
// set up view models
amtrakCascadesViewModel = activity?.run {
@@ -142,7 +172,7 @@ class AmtrakCascadesFragment : DaggerFragment(), Injectable {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- setClosestStationWithPermissionCheck()
+ checkAppPermissions()
}
private fun initDatePicker(){
@@ -183,9 +213,27 @@ class AmtrakCascadesFragment : DaggerFragment(), Injectable {
}
+ // Location Permissions
@SuppressLint("MissingPermission")
@NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- fun setClosestStation() {
+ fun myLocationFine() {
+ context?.let { context ->
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
+ fusedLocationClient.lastLocation
+ .addOnSuccessListener { location : Location? ->
+ location?.let {
+ amtrakCascadesViewModel.selectStationNearestTo(it)
+ }
+ if (location == null) {
+ requestLocationUpdate()
+ }
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ @NeedsPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+ fun myLocationCoarse() {
context?.let { context ->
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationClient.lastLocation
@@ -200,9 +248,58 @@ class AmtrakCascadesFragment : DaggerFragment(), Injectable {
}
}
- @OnShowRationale(Manifest.permission.ACCESS_FINE_LOCATION)
- fun showRationaleForLocation(request: PermissionRequest) {
- showRationaleDialog(R.string.permission_station_location_rationale, request)
+ private fun checkAppPermissions() {
+
+ if (Build.VERSION.SDK_INT == 23) {
+ myLocationFineWithPermissionCheck()
+ } else {
+
+ // Check if app has location permissions granted
+ when (PackageManager.PERMISSION_GRANTED) {
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+ -> {
+ myLocationFineWithPermissionCheck()
+ }
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ }
+ -> {
+ myLocationCoarseWithPermissionCheck()
+ }
+ else -> {
+
+ // show permission rational dialog
+ if (ActivityCompat.shouldShowRequestPermissionRationale(
+ context as Activity,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ ) {
+ AlertDialog.Builder(context!!)
+ .setTitle("Location Permission")
+ .setMessage(R.string.permission_station_location_rationale)
+ .setCancelable(false)
+ .setPositiveButton("next")
+ { _, _ ->
+ locationPermissionRequest.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ )
+ }
+ .show()
+ }
+ }
+ }
+ }
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
@@ -211,25 +308,14 @@ class AmtrakCascadesFragment : DaggerFragment(), Injectable {
onRequestPermissionsResult(requestCode, grantResults)
}
- private fun showRationaleDialog(rationMessage: Int, permRequest: PermissionRequest) {
- context?.let {
- val builder = AlertDialog.Builder(it)
- builder.setTitle("Location Permission")
- builder.setMessage(rationMessage)
- builder.setCancelable(false)
- builder.setPositiveButton("next") { _, _ -> permRequest.proceed()}
- val dialog: AlertDialog = builder.create()
- dialog.show()
- }
- }
-
+ @SuppressLint("MissingPermission")
private fun requestLocationUpdate() {
val locationRequest = LocationRequest()
locationRequest.numUpdates = 1
val locationCallback = object : LocationCallback() {
- override fun onLocationResult(locationResult: LocationResult?) {
+ override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
locationResult.locations.first()?.let {
amtrakCascadesViewModel.selectStationNearestTo(it)
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/bordercrossings/crossingtimes/BaseCrossingTimesFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/bordercrossings/crossingtimes/BaseCrossingTimesFragment.kt
index 3cc0248c..eb8f7e3c 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/bordercrossings/crossingtimes/BaseCrossingTimesFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/bordercrossings/crossingtimes/BaseCrossingTimesFragment.kt
@@ -46,8 +46,10 @@ abstract class BaseCrossingTimesFragment : DaggerFragment(), Injectable {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_border))
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_border))
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
borderCrossingViewModel = ViewModelProvider(this, viewModelFactory)
.get(BorderCrossingViewModel::class.java)
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/cameras/CameraFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/cameras/CameraFragment.kt
index dc92360d..c98aea95 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/cameras/CameraFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/cameras/CameraFragment.kt
@@ -117,7 +117,7 @@ class CameraFragment : DaggerFragment(), Injectable, OnMapReadyCallback {
t?.cancel()
}
- override fun onMapReady(map: GoogleMap?) {
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/common/binding/BindingAdapters.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/common/binding/BindingAdapters.kt
index 1ae03ab0..dae45f5d 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/common/binding/BindingAdapters.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/common/binding/BindingAdapters.kt
@@ -19,7 +19,9 @@ package gov.wa.wsdot.android.wsdot.ui.common.binding
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.Color
+import android.os.Build
import android.text.Html
+import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.*
import androidx.databinding.BindingAdapter
@@ -213,7 +215,12 @@ object BindingAdapters {
if (text != null) {
if (text != "") {
- textView.text = stripHtml(text).trimEnd()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ textView.text = Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY).trimEnd()
+ textView.movementMethod = LinkMovementMethod.getInstance()
+ } else {
+ textView.text = stripHtml(text).trimEnd()
+ }
}
}
}
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/favorites/FavoritesFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/favorites/FavoritesFragment.kt
index 38f36373..0242a7e5 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/favorites/FavoritesFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/favorites/FavoritesFragment.kt
@@ -468,25 +468,34 @@ class FavoritesFragment : DaggerFragment(), AdapterDataSetChangedListener, Injec
private fun navigateToCamera(camera: Camera){
val action = NavGraphDirections.actionGlobalNavCameraFragment(camera.cameraId, camera.title)
- val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_traffic))
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_traffic))
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
+
findNavController().navigate(action)
}
private fun navigateToSchedule(schedule: FerrySchedule) {
val action = NavGraphDirections.actionGlobalNavFerriesRouteFragment(schedule.routeId, schedule.description)
- val adTargets = mapOf(
- "wsdotapp" to resources.getString(R.string.ad_target_ferries),
- "wsdotferries" to AdTargets.getFerryAdTarget(schedule.routeId)
- )
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf(
+// "wsdotapp" to resources.getString(R.string.ad_target_ferries),
+// "wsdotferries" to AdTargets.getFerryAdTarget(schedule.routeId)
+// )
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
+
findNavController().navigate(action)
}
private fun navigateToMountainPass(pass: MountainPass) {
val action = NavGraphDirections.actionGlobalNavMountainPassReportFragment(pass.passId, pass.passName)
- val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_passes))
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_passes))
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
+
findNavController().navigate(action)
}
@@ -541,8 +550,10 @@ class FavoritesFragment : DaggerFragment(), AdapterDataSetChangedListener, Injec
val action = FavoritesFragmentDirections.actionNavFavoritesFragmentToNavFavoriteTrafficMapFragment()
- val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_traffic))
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_traffic))
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
findNavController().navigate(action)
}
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/FerriesHomeFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/FerriesHomeFragment.kt
index 3cae67df..c391bb32 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/FerriesHomeFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/FerriesHomeFragment.kt
@@ -62,11 +62,13 @@ class FerriesHomeFragment : DaggerFragment(), Injectable {
savedInstanceState: Bundle?
): View? {
- val adTargets = mapOf(
- "wsdotapp" to resources.getString(R.string.ad_target_ferries),
- "wsdotferries" to AdTargets.getFerryAdTarget(null)
- )
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf(
+// "wsdotapp" to resources.getString(R.string.ad_target_ferries),
+// "wsdotferries" to AdTargets.getFerryAdTarget(null)
+// )
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
ferriesViewModel = ViewModelProvider(this, viewModelFactory)
.get(FerriesViewModel::class.java)
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/route/FerriesRouteFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/route/FerriesRouteFragment.kt
index ebae39cd..84b05272 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/route/FerriesRouteFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/route/FerriesRouteFragment.kt
@@ -2,13 +2,20 @@ package gov.wa.wsdot.android.wsdot.ui.ferries.route
import android.Manifest
import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.pm.PackageManager
import android.location.Location
+import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.*
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentStatePagerAdapter
@@ -39,8 +46,6 @@ import gov.wa.wsdot.android.wsdot.util.AdTargets
import gov.wa.wsdot.android.wsdot.util.autoCleared
import gov.wa.wsdot.android.wsdot.util.putDouble
import permissions.dispatcher.NeedsPermission
-import permissions.dispatcher.OnShowRationale
-import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
import java.util.*
import java.util.Calendar.*
@@ -81,6 +86,29 @@ class FerriesRouteFragment : DaggerFragment(), Injectable {
private lateinit var fusedLocationClient: FusedLocationProviderClient
+ @RequiresApi(Build.VERSION_CODES.N)
+ val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ when {
+ permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
+ myLocationFineWithPermissionCheck()
+ println("Precise location access granted.")
+
+ }
+ permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
+ myLocationCoarseWithPermissionCheck()
+ println("Coarse location access granted.")
+
+ }
+ else -> {
+ println("No location access granted.")
+ }
+ }
+ }
+
+
+
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as MainActivity).setScreenName(this::class.java.simpleName)
@@ -103,11 +131,13 @@ class FerriesRouteFragment : DaggerFragment(), Injectable {
savedInstanceState: Bundle?
): View? {
- val adTargets = mapOf(
- "wsdotapp" to resources.getString(R.string.ad_target_ferries),
- "wsdotferries" to AdTargets.getFerryAdTarget(args.routeId)
- )
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf(
+// "wsdotapp" to resources.getString(R.string.ad_target_ferries),
+// "wsdotferries" to AdTargets.getFerryAdTarget(args.routeId)
+// )
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
// set up view models
routeViewModel = ViewModelProvider(this, viewModelFactory)
@@ -210,7 +240,7 @@ class FerriesRouteFragment : DaggerFragment(), Injectable {
val tabLayout: TabLayout = view.findViewById(R.id.tab_layout)
tabLayout.setupWithViewPager(viewPager)
- setClosestTerminalWithPermissionCheck()
+ checkAppPermissions()
}
@@ -260,9 +290,10 @@ class FerriesRouteFragment : DaggerFragment(), Injectable {
}
+ // Location Permissions
@SuppressLint("MissingPermission")
@NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- fun setClosestTerminal() {
+ fun myLocationFine() {
context?.let { context ->
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationClient.lastLocation
@@ -277,36 +308,92 @@ class FerriesRouteFragment : DaggerFragment(), Injectable {
}
}
- @OnShowRationale(Manifest.permission.ACCESS_FINE_LOCATION)
- fun showRationaleForLocation(request: PermissionRequest) {
- showRationaleDialog(R.string.permission_terminal_location_rationale, request)
+ @SuppressLint("MissingPermission")
+ @NeedsPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+ fun myLocationCoarse() {
+ context?.let { context ->
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
+ fusedLocationClient.lastLocation
+ .addOnSuccessListener { location : Location? ->
+ location?.let {
+ routeViewModel.selectTerminalNearestTo(it)
+ }
+ if (location == null) {
+ requestLocationUpdate()
+ }
+ }
+ }
}
+ private fun checkAppPermissions() {
+
+ if (Build.VERSION.SDK_INT == 23) {
+ myLocationFineWithPermissionCheck()
+ } else {
+
+ // Check if app has location permissions granted
+ when (PackageManager.PERMISSION_GRANTED) {
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+ -> {
+ myLocationFineWithPermissionCheck()
+ }
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ }
+ -> {
+ myLocationCoarseWithPermissionCheck()
+ }
+ else -> {
+
+ // show permission rational dialog
+ if (ActivityCompat.shouldShowRequestPermissionRationale(
+ context as Activity,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ ) {
+ AlertDialog.Builder(context!!)
+ .setTitle("Location Permission")
+ .setMessage(R.string.permission_terminal_location_rationale)
+ .setCancelable(false)
+ .setPositiveButton("next")
+ { _, _ ->
+ locationPermissionRequest.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ )
+ }
+ .show()
+ }
+ }
+ }
+ }
+ }
+
+
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// NOTE: delegate the permission handling to generated function
onRequestPermissionsResult(requestCode, grantResults)
}
- private fun showRationaleDialog(rationMessage: Int, permRequest: PermissionRequest) {
- context?.let {
- val builder = AlertDialog.Builder(it)
- builder.setTitle("Location Permission")
- builder.setMessage(rationMessage)
- builder.setCancelable(false)
- builder.setPositiveButton("next") { _, _ -> permRequest.proceed()}
- val dialog: AlertDialog = builder.create()
- dialog.show()
- }
- }
-
+ @SuppressLint("MissingPermission")
private fun requestLocationUpdate() {
val locationRequest = LocationRequest()
locationRequest.numUpdates = 1
val locationCallback = object : LocationCallback() {
- override fun onLocationResult(locationResult: LocationResult?) {
+ override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
locationResult.locations.first()?.let {
routeViewModel.selectTerminalNearestTo(it)
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/vesselwatch/VesselWatchFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/vesselwatch/VesselWatchFragment.kt
index e5447373..3dcc6e88 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/vesselwatch/VesselWatchFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/ferries/vesselwatch/VesselWatchFragment.kt
@@ -2,8 +2,10 @@ package gov.wa.wsdot.android.wsdot.ui.ferries.vesselwatch
import android.Manifest
import android.annotation.SuppressLint
+import android.content.pm.PackageManager
import android.content.res.Resources
import android.location.Location
+import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -14,13 +16,14 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
-import androidx.appcompat.app.AlertDialog
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
-import com.google.android.gms.location.FusedLocationProviderClient
-import com.google.android.gms.location.LocationServices
+import com.google.android.gms.location.*
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
@@ -42,8 +45,6 @@ import gov.wa.wsdot.android.wsdot.util.getDouble
import gov.wa.wsdot.android.wsdot.model.common.Status
import gov.wa.wsdot.android.wsdot.util.putDouble
import permissions.dispatcher.NeedsPermission
-import permissions.dispatcher.OnShowRationale
-import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
import javax.inject.Inject
@@ -77,6 +78,28 @@ class VesselWatchFragment: DaggerFragment(), Injectable, OnMapReadyCallback, Goo
}
}
+ @RequiresApi(Build.VERSION_CODES.N)
+ val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ when {
+ permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
+ myLocationFineWithPermissionCheck()
+ println("Precise location access granted.")
+
+ }
+ permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
+ myLocationCoarseWithPermissionCheck()
+ println("Coarse location access granted.")
+
+ }
+ else -> {
+ println("No location access granted.")
+ }
+ }
+ }
+
+
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as MainActivity).setScreenName(this::class.java.simpleName)
@@ -124,7 +147,7 @@ class VesselWatchFragment: DaggerFragment(), Injectable, OnMapReadyCallback, Goo
}
- override fun onMapReady(map: GoogleMap?) {
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
@@ -146,7 +169,7 @@ class VesselWatchFragment: DaggerFragment(), Injectable, OnMapReadyCallback, Goo
}
}
- enableMyLocationWithPermissionCheck()
+ checkAppPermissions()
val settings = PreferenceManager.getDefaultSharedPreferences(activity)
@@ -348,21 +371,71 @@ class VesselWatchFragment: DaggerFragment(), Injectable, OnMapReadyCallback, Goo
// Location Permission
@SuppressLint("MissingPermission")
@NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- fun enableMyLocation() {
+ fun myLocationFine() {
context?.let { context ->
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationClient.lastLocation
- .addOnSuccessListener { location : Location? ->
+ .addOnSuccessListener { location: Location? ->
location?.let {
mMap?.isMyLocationEnabled = true
+ requestLocationUpdates()
+ }
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ @NeedsPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+ fun myLocationCoarse() {
+ context?.let { context ->
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
+ fusedLocationClient.lastLocation
+ .addOnSuccessListener { location : Location? ->
+ location?.let {
+ mMap?.isMyLocationEnabled = false
+ requestLocationUpdates()
}
}
}
}
- @OnShowRationale(Manifest.permission.ACCESS_FINE_LOCATION)
- fun showRationaleForLocation(request: PermissionRequest) {
- showRationaleDialog(R.string.permission_map_location_rationale, request)
+ private fun checkAppPermissions() {
+
+ if (Build.VERSION.SDK_INT == 23) {
+ myLocationFineWithPermissionCheck()
+ } else {
+
+ // Check if app has location permissions granted
+ when (PackageManager.PERMISSION_GRANTED) {
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+ -> {
+ myLocationFineWithPermissionCheck()
+ }
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ }
+ -> {
+ myLocationCoarseWithPermissionCheck()
+ }
+ else -> {
+ // Present permission dialog to request permission type
+ locationPermissionRequest.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ )
+ }
+ }
+ }
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
@@ -370,16 +443,20 @@ class VesselWatchFragment: DaggerFragment(), Injectable, OnMapReadyCallback, Goo
onRequestPermissionsResult(requestCode, grantResults)
}
- private fun showRationaleDialog(rationMessage: Int, permRequest: PermissionRequest) {
- context?.let {
- val builder = AlertDialog.Builder(it)
- builder.setTitle("Location Permission")
- builder.setMessage(rationMessage)
- builder.setCancelable(false)
- builder.setPositiveButton("next") { _, _ -> permRequest.proceed()}
- val dialog: AlertDialog = builder.create()
- dialog.show()
+ @SuppressLint("MissingPermission")
+ private fun requestLocationUpdates() {
+
+ val locationRequest = LocationRequest()
+ locationRequest.numUpdates = 1
+ val locationCallback = object : LocationCallback() {
+ override fun onLocationResult(locationResult: LocationResult) {
+ locationResult ?: return
+ }
}
- }
+ fusedLocationClient.requestLocationUpdates(
+ locationRequest,
+ locationCallback,
+ Looper.getMainLooper())
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/highwayAlerts/HighwayAlertFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/highwayAlerts/HighwayAlertFragment.kt
index 7970e05b..8da0e323 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/highwayAlerts/HighwayAlertFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/highwayAlerts/HighwayAlertFragment.kt
@@ -95,7 +95,7 @@ class HighwayAlertFragment : DaggerFragment(), Injectable, OnMapReadyCallback {
return dataBinding.root
}
- override fun onMapReady(map: GoogleMap?) {
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/mountainpasses/MountainPassHomeFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/mountainpasses/MountainPassHomeFragment.kt
index 5b1bfc9f..b3603424 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/mountainpasses/MountainPassHomeFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/mountainpasses/MountainPassHomeFragment.kt
@@ -54,8 +54,10 @@ class MountainPassHomeFragment : DaggerFragment(), Injectable {
savedInstanceState: Bundle?
): View? {
- val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_passes))
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_passes))
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
passViewModel = ViewModelProvider(this, viewModelFactory)
.get(MountainPassViewModel::class.java)
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/tollrates/tollsigns/TollTripFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/tollrates/tollsigns/TollTripFragment.kt
index d506e4c9..4e0a7c85 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/tollrates/tollsigns/TollTripFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/tollrates/tollsigns/TollTripFragment.kt
@@ -49,7 +49,7 @@ class TollTripFragment : DaggerFragment(), Injectable, OnMapReadyCallback {
return dataBinding.root
}
- override fun onMapReady(map: GoogleMap?) {
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
mMap.uiSettings.isMapToolbarEnabled = false
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/TrafficMapFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/TrafficMapFragment.kt
index b225768c..7cb25e39 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/TrafficMapFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/TrafficMapFragment.kt
@@ -2,10 +2,12 @@ package gov.wa.wsdot.android.wsdot.ui.trafficmap
import android.Manifest
import android.annotation.SuppressLint
+import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Color
import android.location.Location
+import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -17,18 +19,23 @@ import android.view.*
import android.widget.EditText
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
+import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.google.android.gms.location.*
+import com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY
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.*
+import com.google.android.gms.tasks.CancellationTokenSource
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
@@ -57,10 +64,7 @@ import gov.wa.wsdot.android.wsdot.util.*
import gov.wa.wsdot.android.wsdot.util.map.CameraClusterManager
import gov.wa.wsdot.android.wsdot.util.map.CameraRenderer
import permissions.dispatcher.NeedsPermission
-import permissions.dispatcher.OnShowRationale
-import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.RuntimePermissions
-import java.lang.Boolean.getBoolean
import java.util.*
import javax.inject.Inject
import kotlin.collections.HashMap
@@ -99,6 +103,7 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
var showAlerts: Boolean = true
var showRestAreas: Boolean = true
+ var requestLocationUpgrade: Boolean = true
private lateinit var mMap: GoogleMap
@@ -115,6 +120,12 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
// Camera update task timer
var t: Timer? = null
+ // Approximate location radius circle
+ private var radiusCircle: Circle? = null
+
+ // Current location cancellation token
+ private var cancellationTokenSource = CancellationTokenSource()
+
// FAB
private lateinit var mFab: SpeedDialView
@@ -127,6 +138,27 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
}
}
+ // Determine which permissions have been granted
+ @RequiresApi(Build.VERSION_CODES.N)
+ val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ when {
+ permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
+ myLocationFineWithPermissionCheck()
+ println("Precise location access granted.")
+
+ }
+ permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
+ myLocationCoarseWithPermissionCheck()
+ println("Coarse location access granted.")
+
+ } else -> {
+ println("No location access granted.")
+ }
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
@@ -137,8 +169,10 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
- val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_traffic))
- (activity as MainActivity).enableAds(adTargets)
+// val adTargets = mapOf("wsdotapp" to resources.getString(R.string.ad_target_traffic))
+// (activity as MainActivity).enableAds(adTargets)
+
+ (activity as MainActivity).disableAds()
(activity as MainActivity).setScreenName(this::class.java.simpleName)
// Inflate the layout for this fragment
@@ -245,17 +279,23 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
super.onCreateOptionsMenu(menu, inflater)
}
+ // Check location permissions when menu item is selected
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_my_location -> {
- goToMyLocationWithPermissionCheck()
+ if (requestLocationUpgrade) {
+ myLocationFineWithPermissionCheck()
+ }
+ checkAppPermissions()
}
- else -> {}
}
+
return false
+
}
- override fun onMapReady(map: GoogleMap?) {
+
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
@@ -279,7 +319,7 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
mMap.clear()
- enableMyLocationWithPermissionCheck()
+ checkAppPermissions()
mMap.uiSettings.isCompassEnabled = true
mMap.uiSettings.isMyLocationButtonEnabled = false
@@ -987,33 +1027,88 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
@SuppressLint("MissingPermission")
@NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- fun enableMyLocation() {
+ fun myLocationFine() {
+ radiusCircle?.remove()
context?.let { context ->
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
mMap.isMyLocationEnabled = true
requestLocationUpdates()
+ requestGoToLocationUpdate()
}
}
}
@SuppressLint("MissingPermission")
- @NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- fun goToMyLocation() {
+ @NeedsPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+ fun myLocationCoarse() {
context?.let { context ->
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
- fusedLocationClient.lastLocation
- .addOnSuccessListener { location : Location? ->
- mMap.isMyLocationEnabled = true
- requestGoToLocationUpdate()
+ fusedLocationClient.getCurrentLocation(PRIORITY_HIGH_ACCURACY, cancellationTokenSource.token).addOnSuccessListener { location : Location? ->
+ mMap.isMyLocationEnabled = false
+ if(location != null) {
+ circle(location)
}
+ }
+ requestCoarseLocationUpdate()
}
}
- @OnShowRationale(Manifest.permission.ACCESS_FINE_LOCATION)
- fun showRationaleForLocation(request: PermissionRequest) {
- showRationaleDialog(R.string.permission_map_location_rationale, request)
+ private fun circle(location: Location) {
+ val circleOptions = CircleOptions()
+ .center(LatLng(location.latitude, location.longitude))
+ .radius(location.accuracy.toDouble())
+ .strokeWidth(5F)
+ .strokeColor(0x3571cce7)
+ .fillColor(0x3571cce7)
+ radiusCircle?.remove()
+ radiusCircle = mMap.addCircle(circleOptions)
+ }
+
+ private fun checkAppPermissions() {
+
+ // API 23 requires fine location alert dialog
+ if (Build.VERSION.SDK_INT == 23) {
+ myLocationFineWithPermissionCheck()
+ } else {
+
+ // Check if app has location permissions granted
+ when (PackageManager.PERMISSION_GRANTED) {
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+ -> {
+ myLocationFineWithPermissionCheck()
+ }
+ activity?.let {
+ ContextCompat.checkSelfPermission(
+ it,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ }
+ -> {
+ myLocationCoarseWithPermissionCheck()
+
+ if (Build.VERSION.SDK_INT > 30) {
+ requestLocationUpgrade = false
+ }
+
+ }
+ else -> {
+ // Present permission dialog to request permission type
+ locationPermissionRequest.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ )
+ }
+ }
+ }
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
@@ -1021,25 +1116,37 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
onRequestPermissionsResult(requestCode, grantResults)
}
- private fun showRationaleDialog(rationMessage: Int, permRequest: PermissionRequest) {
- context?.let {
- val builder = AlertDialog.Builder(it)
- builder.setTitle("Location Permission")
- builder.setMessage(rationMessage)
- builder.setCancelable(false)
- builder.setPositiveButton("next") { _, _ -> permRequest.proceed()}
- val dialog: AlertDialog = builder.create()
- dialog.show()
+ @SuppressLint("MissingPermission")
+ private fun requestCoarseLocationUpdate() {
+
+ val locationRequest = LocationRequest()
+ locationRequest.numUpdates = 1
+
+ // Request location update every 60 seconds
+ locationRequest.interval = 60000
+
+ val locationCallback = object : LocationCallback() {
+ override fun onLocationResult(locationResult: LocationResult) {
+ locationResult ?: return
+ goToUsersLocation(locationResult.lastLocation)
+ }
}
+
+ fusedLocationClient.requestLocationUpdates(
+ locationRequest,
+ locationCallback,
+ Looper.getMainLooper())
}
+
+ @SuppressLint("MissingPermission")
private fun requestGoToLocationUpdate() {
val locationRequest = LocationRequest()
locationRequest.numUpdates = 1
val locationCallback = object : LocationCallback() {
- override fun onLocationResult(locationResult: LocationResult?) {
+ override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
goToUsersLocation(locationResult.lastLocation)
}
@@ -1055,12 +1162,13 @@ class TrafficMapFragment : DaggerFragment(), Injectable, OnMapReadyCallback,
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location.latitude, location.longitude), 15.0f))
}
+ @SuppressLint("MissingPermission")
private fun requestLocationUpdates() {
val locationRequest = LocationRequest()
locationRequest.numUpdates = 1
val locationCallback = object : LocationCallback() {
- override fun onLocationResult(locationResult: LocationResult?) {
+ override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
checkSpeed(locationResult.lastLocation)
}
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/restareas/RestAreaFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/restareas/RestAreaFragment.kt
index 944e6d29..c7685c28 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/restareas/RestAreaFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/restareas/RestAreaFragment.kt
@@ -74,7 +74,7 @@ class RestAreaFragment: DaggerFragment(), Injectable, OnMapReadyCallback {
return dataBinding.root
}
- override fun onMapReady(map: GoogleMap?) {
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
diff --git a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/travelerinformation/bridgeAlerts/BridgeAlertFragment.kt b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/travelerinformation/bridgeAlerts/BridgeAlertFragment.kt
index a038b687..2140ad9a 100644
--- a/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/travelerinformation/bridgeAlerts/BridgeAlertFragment.kt
+++ b/app/src/main/java/gov/wa/wsdot/android/wsdot/ui/trafficmap/travelerinformation/bridgeAlerts/BridgeAlertFragment.kt
@@ -88,7 +88,7 @@ class BridgeAlertFragment : DaggerFragment(), Injectable, OnMapReadyCallback {
return dataBinding.root
}
- override fun onMapReady(map: GoogleMap?) {
+ override fun onMapReady(map: GoogleMap) {
mMap = map as GoogleMap
diff --git a/app/src/main/res/layout/about_fragment.xml b/app/src/main/res/layout/about_fragment.xml
index a222df9f..f35cd523 100644
--- a/app/src/main/res/layout/about_fragment.xml
+++ b/app/src/main/res/layout/about_fragment.xml
@@ -1,5 +1,6 @@
-
-
-
-
+ android:layout_width="match_parent"
+ android:background="?android:attr/selectableItemBackground">
-
+
-
+
-
+
-
+
-
+
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index d8aec453..a8c30be3 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -50,6 +50,7 @@
android:paddingTop="8dp"
android:gravity="center_horizontal"
android:orientation="horizontal"
+ android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
diff --git a/app/src/main/res/layout/ferry_alert_item.xml b/app/src/main/res/layout/ferry_alert_item.xml
index fcfedbab..44189d9d 100644
--- a/app/src/main/res/layout/ferry_alert_item.xml
+++ b/app/src/main/res/layout/ferry_alert_item.xml
@@ -49,7 +49,6 @@
app:layout_constraintTop_toBottomOf="@id/titleView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- android:autoLink="all"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:layout_marginTop="16dp"
diff --git a/app/src/main/res/layout/map_fragment.xml b/app/src/main/res/layout/map_fragment.xml
index 2f061bb7..95f6a305 100644
--- a/app/src/main/res/layout/map_fragment.xml
+++ b/app/src/main/res/layout/map_fragment.xml
@@ -60,8 +60,6 @@
android:id="@+id/camera_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:maxWidth="@dimen/camera_sheet_width"
- android:maxHeight="@dimen/camera_sheet_height"
android:clickable="true"
android:focusable="true"
android:layout_gravity="start"
@@ -83,7 +81,6 @@
android:id="@+id/highway_alert_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:maxWidth="500dp"
android:clickable="true"
android:focusable="true"
android:layout_gravity="start"
diff --git a/app/src/main/res/layout/vessel_watch.xml b/app/src/main/res/layout/vessel_watch.xml
index 623ab026..f553236a 100644
--- a/app/src/main/res/layout/vessel_watch.xml
+++ b/app/src/main/res/layout/vessel_watch.xml
@@ -43,8 +43,6 @@
android:id="@+id/camera_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:maxWidth="@dimen/camera_sheet_width"
- android:maxHeight="@dimen/camera_sheet_height"
android:clickable="true"
android:focusable="true"
android:layout_gravity="start"
diff --git a/build.gradle b/build.gradle
index 5242e2cf..103e7d29 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.4.21'
+ ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
@@ -9,9 +9,11 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.2"
- classpath 'com.google.gms:google-services:4.3.4'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
+ classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
+ classpath 'com.google.gms:google-services:4.3.10'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
+
+ // Updating to 2.0.0 causes error. Manifest merger failed.
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:1.2.0"
}