diff --git a/app/build.gradle b/app/build.gradle index 8e5b9a43c..c30d5857c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,8 +42,8 @@ android { applicationId "com.geeksville.mesh" minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works) targetSdkVersion 30 // 30 can't work until an explicit location permissions dialog is added - versionCode 20253 // format is Mmmss (where M is 1+the numeric major number - versionName "1.2.53" + versionCode 20254 // format is Mmmss (where M is 1+the numeric major number + versionName "1.2.54" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // per https://developer.android.com/studio/write/vector-asset-studio @@ -121,16 +121,16 @@ protobuf { dependencies { - def room_version = '2.4.0' + def room_version = '2.4.1' implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.4.0' + implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.fragment:fragment-ktx:1.4.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.2' - implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.viewpager2:viewpager2:1.0.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0' @@ -171,10 +171,10 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.0' // location services - implementation 'com.google.android.gms:play-services-location:19.0.0' + implementation 'com.google.android.gms:play-services-location:19.0.1' // For Google Sign-In (owner name accesss) - implementation 'com.google.android.gms:play-services-auth:20.0.0' + implementation 'com.google.android.gms:play-services-auth:20.0.1' // Add the Firebase SDK for Crashlytics. implementation 'com.google.firebase:firebase-crashlytics:18.2.6' diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index b3509b158..02677e207 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -298,13 +298,13 @@ class MainActivity : AppCompatActivity(), Logging, fun requestScanPermission() = requestPermission(getScanPermissions(), true) /** Ask the user to grant camera permission */ - fun requestCameraPermission() = requestPermission(getCameraPermissions(), false) + fun requestCameraPermission() = requestPermission(getCameraPermissions()) /** Ask the user to grant foreground location permission */ - fun requestLocationPermission() = requestPermission(getLocationPermissions(), false) + fun requestLocationPermission() = requestPermission(getLocationPermissions()) /** Ask the user to grant background location permission */ - fun requestBackgroundPermission() = requestPermission(getBackgroundPermissions(), false) + fun requestBackgroundPermission() = requestPermission(getBackgroundPermissions()) /** * @return a localized string warning user about missing permissions. Or null if everything is find @@ -344,7 +344,7 @@ class MainActivity : AppCompatActivity(), Logging, */ private fun requestPermission( missingPerms: List = getMinimumPermissions(), - shouldShowDialog: Boolean = true + shouldShowDialog: Boolean = false ): Boolean = if (missingPerms.isNotEmpty()) { val shouldShow = missingPerms.filter { diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index b0ac61244..a326ca8ec 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -135,7 +135,7 @@ class MeshService : Service(), Logging { } private val locationCallback = MeshServiceLocationCallback( - ::perhapsSendPosition, + ::sendPositionScoped, onSendPositionFailed = { onConnectionChanged(ConnectionState.DEVICE_SLEEP) }, getNodeNum = { myNodeNum } ) @@ -170,7 +170,7 @@ class MeshService : Service(), Logging { * We first check to see if our local device has already sent a position and if so, we punt until the next check. * This allows us to only 'fill in' with GPS positions when the local device happens to have no good GPS sats. */ - private fun perhapsSendPosition( + private fun sendPositionScoped( lat: Double = 0.0, lon: Double = 0.0, alt: Int = 0, @@ -181,17 +181,7 @@ class MeshService : Service(), Logging { // do most of the work in my service thread serviceScope.handledLaunch { // if android called us too soon, just ignore - - val myInfo = localNodeInfo - val lastLat = (myInfo?.position?.latitude ?: 0.0) - val lastLon = (myInfo?.position?.longitude ?: 0.0) - val lastSendMsec = (myInfo?.position?.time ?: 0) * 1000L - val now = System.currentTimeMillis() - if ((lastLat == 0.0 && lastLon == 0.0) || (now - lastSendMsec > locationIntervalMsec)) // && minBroadcastPeriod ? - sendPosition(lat, lon, alt, destNum, wantResponse) - else { - debug("Not sending position - local node sent ${(now - lastSendMsec) / 1000L}s ago ${myInfo?.position?.toPIIString()}") - } + sendPosition(lat, lon, alt, destNum, wantResponse) } } @@ -1028,10 +1018,10 @@ class MeshService : Service(), Logging { else broadcastSecs * 1000L - // if (prefs.locationShare == RadioConfigProtos.LocationSharing.LocDisabled) { - // info("GPS location sharing is disabled") - // desiredInterval = 0 - // } + if (prefs.locationShare == RadioConfigProtos.LocationSharing.LocDisabled) { + info("GPS location sharing is disabled") + desiredInterval = 0 + } // if (prefs.fixedPosition) { // info("Node has fixed position, therefore not overriding position") @@ -1043,6 +1033,7 @@ class MeshService : Service(), Logging { startLocationRequests(desiredInterval) } else { info("No GPS assistance desired, but sending UTC time to mesh") + warnUserAboutLocation() sendPosition() } } diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceLocationCallback.kt b/app/src/main/java/com/geeksville/mesh/service/MeshServiceLocationCallback.kt index bb7e4bc24..2225756ec 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceLocationCallback.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshServiceLocationCallback.kt @@ -38,9 +38,9 @@ class MeshServiceLocationCallback( if (location.isAccurateForMesh) { // if within 200 meters, or accuracy is unknown try { - // Do we want to broadcast this position globally, or are we just telling the local node what its current position is ( + // Do we want to broadcast this position globally, or are we just telling the local node what its current position is val shouldBroadcast = - true // no need to rate limit, because we are just sending at the interval requested by the preferences + false // no need to rate limit, because we are just sending to the local node val destinationNumber = if (shouldBroadcast) DataPacket.NODENUM_BROADCAST else getNodeNum() diff --git a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt index 16881c2e4..ddd62eadd 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt @@ -28,7 +28,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { _binding = AdvancedSettingsBinding.inflate(inflater, container, false) return binding.root } @@ -39,6 +39,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { model.radioConfig.observe(viewLifecycleOwner, { binding.positionBroadcastPeriodEditText.setText(model.positionBroadcastSecs.toString()) binding.lsSleepEditText.setText(model.lsSleepSecs.toString()) + binding.positionBroadcastPeriodView.isEnabled = model.locationShare ?: true binding.positionBroadcastSwitch.isChecked = model.locationShare ?: true binding.lsSleepView.isEnabled = model.isPowerSaving ?: false binding.lsSleepSwitch.isChecked = model.isPowerSaving ?: false @@ -47,7 +48,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { model.isConnected.observe(viewLifecycleOwner, { connectionState -> val connected = connectionState == MeshService.ConnectionState.CONNECTED - binding.positionBroadcastPeriodView.isEnabled = connected + binding.positionBroadcastPeriodView.isEnabled = connected && model.locationShare ?: true binding.lsSleepView.isEnabled = connected && model.isPowerSaving ?: false binding.positionBroadcastSwitch.isEnabled = connected binding.lsSleepSwitch.isEnabled = connected diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index b19290a2e..9a858122c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -615,22 +615,29 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinner.adapter = regionAdapter - model.bluetoothEnabled.observe( - viewLifecycleOwner, { - if (it) binding.changeRadioButton.show() - else binding.changeRadioButton.hide() - }) + model.bluetoothEnabled.observe(viewLifecycleOwner, { + if (it) binding.changeRadioButton.show() + else binding.changeRadioButton.hide() + }) model.ownerName.observe(viewLifecycleOwner, { name -> binding.usernameEditText.setText(name) }) // Only let user edit their name or set software update while connected to a radio - model.isConnected.observe( - viewLifecycleOwner, { - updateNodeInfo() - updateDevicesButtons(scanModel.devices.value) - }) + model.isConnected.observe(viewLifecycleOwner, { + updateNodeInfo() + updateDevicesButtons(scanModel.devices.value) + }) + + model.radioConfig.observe(viewLifecycleOwner, { + binding.provideLocationCheckbox.isEnabled = + isGooglePlayAvailable(requireContext()) && model.locationShare ?: true + if (model.locationShare == false) { + model.provideLocation.value = false + binding.provideLocationCheckbox.isChecked = false + } + }) // Also watch myNodeInfo because it might change later model.myNodeInfo.observe(viewLifecycleOwner, { @@ -659,7 +666,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { requireActivity().hideKeyboard() } - binding.provideLocationCheckbox.isEnabled = isGooglePlayAvailable(requireContext()) binding.provideLocationCheckbox.setOnCheckedChangeListener { view, isChecked -> if (view.isPressed && isChecked) { // We want to ignore changes caused by code (as opposed to the user) // Don't check the box until the system setting changes @@ -896,7 +902,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { debug("We have location access") } - locationSettingsResponse.addOnFailureListener { _ -> + locationSettingsResponse.addOnFailureListener { errormsg("Failed to get location access") // We always show the toast regardless of what type of exception we receive. Because even non // resolvable api exceptions mean user still needs to fix something. diff --git a/app/src/main/res/drawable/ic_send_24.xml b/app/src/main/res/drawable/ic_send_24.xml deleted file mode 100644 index f0d63e179..000000000 --- a/app/src/main/res/drawable/ic_send_24.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_twotone_send_24.xml b/app/src/main/res/drawable/ic_twotone_send_24.xml new file mode 100644 index 000000000..ee3e89f79 --- /dev/null +++ b/app/src/main/res/drawable/ic_twotone_send_24.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/layout/messages_fragment.xml b/app/src/main/res/layout/messages_fragment.xml index c964a9b6f..411a6d8e0 100644 --- a/app/src/main/res/layout/messages_fragment.xml +++ b/app/src/main/res/layout/messages_fragment.xml @@ -48,6 +48,6 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/textInputLayout" - app:srcCompat="@drawable/ic_send_24" /> + app:srcCompat="@drawable/ic_twotone_send_24" /> diff --git a/build.gradle b/build.gradle index a47645681..b2bfca256 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ buildscript { classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1' // protobuf plugin - docs here https://github.com/google/protobuf-gradle-plugin - classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.15' + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.17' //classpath "app.brant:amazonappstorepublisher:0.1.0" classpath 'com.github.triplet.gradle:play-publisher:2.8.0'