WIP - work with background location being optional

1.2-legacy
Kevin Hester 2021-06-10 10:58:45 -07:00
rodzic e0bbbb3c14
commit 9c1316ea09
5 zmienionych plików z 105 dodań i 66 usunięć

Wyświetl plik

@ -37,8 +37,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 20231 // format is Mmmss (where M is 1+the numeric major number
versionName "1.2.31"
versionCode 20240 // format is Mmmss (where M is 1+the numeric major number
versionName "1.2.40"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// per https://developer.android.com/studio/write/vector-asset-studio

Wyświetl plik

@ -25,6 +25,7 @@ import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
@ -41,6 +42,8 @@ import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.android.ServiceClient
import com.geeksville.concurrent.handledLaunch
import com.geeksville.mesh.android.getBackgroundPermissions
import com.geeksville.mesh.android.getMissingPermissions
import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.databinding.ActivityMainBinding
import com.geeksville.mesh.model.ChannelSet
@ -120,7 +123,6 @@ eventually:
val utf8 = Charset.forName("UTF-8")
class MainActivity : AppCompatActivity(), Logging,
ActivityCompat.OnRequestPermissionsResultCallback {
@ -223,17 +225,9 @@ class MainActivity : AppCompatActivity(), Logging,
model.bluetoothEnabled.value = enabled
}
/**
* return a list of the permissions we don't have
/** Get the minimum permissions our app needs to run correctly
*/
private fun getMissingPermissions(perms: List<String>) = perms.filter {
ContextCompat.checkSelfPermission(
this,
it
) != PackageManager.PERMISSION_GRANTED
}
private fun getMissingPermissions(): List<String> {
private fun getMinimumPermissions(): List<String> {
val perms = mutableListOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
@ -245,9 +239,6 @@ class MainActivity : AppCompatActivity(), Logging,
// Manifest.permission.WRITE_EXTERNAL_STORAGE
)
if (Build.VERSION.SDK_INT >= 29) // only added later
perms.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
// Some old phones complain about requesting perms they don't understand
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
perms.add(Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)
@ -257,10 +248,16 @@ class MainActivity : AppCompatActivity(), Logging,
return getMissingPermissions(perms)
}
private fun requestPermission() {
debug("Checking permissions")
val missingPerms = getMissingPermissions()
/** Ask the user to grant background location permission */
fun requestBackgroundPermission() = requestPermission(getBackgroundPermissions())
/** Possibly prompt user to grant permissions
*
* @return true if we already have the needed permissions
*/
private fun requestPermission(missingPerms: List<String> = getMinimumPermissions()): Boolean =
if (missingPerms.isNotEmpty()) {
missingPerms.forEach {
// Permission is not granted
@ -283,17 +280,20 @@ class MainActivity : AppCompatActivity(), Logging,
// DID_REQUEST_PERM is an
// app-defined int constant. The callback method gets the
// result of the request.
error("Permissions missing, asked user to grant")
false
} else {
// Permission has already been granted
debug("We have our required permissions")
true
}
}
/**
* Remind user he's disabled permissions we need
*
* @return true if we did warn
*/
@SuppressLint("InlinedApi") // This function is careful to work with old APIs correctly
fun warnMissingPermissions(): Boolean {
// Older versions of android don't know about these permissions - ignore failure to grant
val ignoredPermissions = setOf(
@ -302,7 +302,7 @@ class MainActivity : AppCompatActivity(), Logging,
Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
)
val deniedPermissions = getMissingPermissions().filter { name ->
val deniedPermissions = getMinimumPermissions().filter { name ->
!ignoredPermissions.contains(name)
}

Wyświetl plik

@ -1,9 +1,13 @@
package com.geeksville.mesh.android
import android.Manifest
import android.app.NotificationManager
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.usb.UsbManager
import android.os.Build
import androidx.core.content.ContextCompat
/**
* @return null on platforms without a BlueTooth driver (i.e. the emulator)
@ -13,3 +17,28 @@ val Context.bluetoothManager: BluetoothManager? get() = getSystemService(Context
val Context.usbManager: UsbManager get() = requireNotNull(getSystemService(Context.USB_SERVICE) as? UsbManager?) { "USB_SERVICE is not available"}
val Context.notificationManager: NotificationManager get() = requireNotNull(getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager?)
/**
* return a list of the permissions we don't have
*/
fun Context.getMissingPermissions(perms: List<String>) = perms.filter {
ContextCompat.checkSelfPermission(
this,
it
) != PackageManager.PERMISSION_GRANTED
}
/**
* A list of missing background location permissions (or empty if we already have what we need)
*/
fun Context.getBackgroundPermissions(): List<String> {
val perms = mutableListOf<String>()
if (Build.VERSION.SDK_INT >= 29) // only added later
perms.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
return getMissingPermissions(perms)
}
/** @return true if the user already has background location permission */
fun Context.hasBackgroundPermission() = getBackgroundPermissions().isEmpty()

Wyświetl plik

@ -20,6 +20,7 @@ import com.geeksville.concurrent.handledLaunch
import com.geeksville.mesh.*
import com.geeksville.mesh.MeshProtos.MeshPacket
import com.geeksville.mesh.MeshProtos.ToRadio
import com.geeksville.mesh.android.hasBackgroundPermission
import com.geeksville.mesh.database.MeshtasticDatabase
import com.geeksville.mesh.database.PacketRepository
import com.geeksville.mesh.database.entity.Packet
@ -200,7 +201,7 @@ class MeshService : Service(), Logging {
@UiThread
private fun startLocationRequests(requestInterval: Long) {
// FIXME - currently we don't support location reading without google play
if (fusedLocationClient == null && isGooglePlayAvailable(this)) {
if (fusedLocationClient == null && hasBackgroundPermission() && isGooglePlayAvailable(this)) {
GeeksvilleApplication.analytics.track("location_start") // Figure out how many users needed to use the phone GPS
locationIntervalMsec = requestInterval

Wyświetl plik

@ -13,21 +13,7 @@
android:layout_height="0dp"
android:layout_marginTop="16dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/warningNotPaired"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:autoLink="web"
android:ems="10"
android:gravity="start|top"
android:text="@string/warning_not_paired"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deviceRadioGroup" />
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/nodeSettings"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -125,34 +111,6 @@
android:text="@string/test_devname2" />
</RadioGroup>
<CheckBox
android:id="@+id/analyticsOkayCheckbox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@android:color/transparent"
android:checked="true"
android:text="@string/analytics_okay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/reportBugButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/warningNotPaired"
app:layout_constraintVertical_bias="1.0" />
<com.google.android.material.button.MaterialButton
android:id="@+id/reportBugButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="@string/report_bug"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.4" />
<com.google.android.material.button.MaterialButton
android:id="@+id/changeRadioButton"
android:layout_width="wrap_content"
@ -182,5 +140,56 @@
app:layout_constraintStart_toStartOf="@+id/updateFirmwareButton"
app:layout_constraintTop_toBottomOf="@+id/updateFirmwareButton" />
<CheckBox
android:id="@+id/provideLocationCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Provide location to mesh"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deviceRadioGroup" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/warningNotPaired"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:autoLink="web"
android:ems="10"
android:gravity="start|top"
android:text="@string/warning_not_paired"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/provideLocationCheckbox" />
<CheckBox
android:id="@+id/analyticsOkayCheckbox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@android:color/transparent"
android:checked="true"
android:text="@string/analytics_okay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/reportBugButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/warningNotPaired"
app:layout_constraintVertical_bias="1.0" />
<com.google.android.material.button.MaterialButton
android:id="@+id/reportBugButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="@string/report_bug"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.4" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>