sforkowany z mirror/meshtastic-android
WIP - work with background location being optional
rodzic
e0bbbb3c14
commit
9c1316ea09
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -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>
|
Ładowanie…
Reference in New Issue