kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
remove play services
rodzic
f282f3f71e
commit
188d1fddaa
|
@ -3,12 +3,12 @@ apply plugin: 'kotlin-android'
|
|||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.github.triplet.play'
|
||||
apply plugin: 'de.mobilej.unmock'
|
||||
// apply plugin: "app.brant.amazonappstorepublisher"
|
||||
|
||||
// Apply the Crashlytics Gradle plugin
|
||||
// Firebase Crashlytics
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
// protobuf
|
||||
|
@ -38,11 +38,10 @@ android {
|
|||
}
|
||||
compileSdkVersion 31
|
||||
// leave undefined to use version plugin wants
|
||||
// buildToolsVersion "30.0.2" // Note: 30.0.2 doesn't yet work on Github actions CI
|
||||
defaultConfig {
|
||||
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
|
||||
targetSdkVersion 30
|
||||
versionCode 20339 // format is Mmmss (where M is 1+the numeric major number
|
||||
versionName "1.3.39"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
@ -169,18 +168,10 @@ dependencies {
|
|||
// implementation 'com.google.android.things:androidthings:1.0'
|
||||
implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
|
||||
|
||||
// location services
|
||||
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.2.0'
|
||||
|
||||
// Add the Firebase SDK for Crashlytics.
|
||||
implementation 'com.google.firebase:firebase-crashlytics:18.2.6'
|
||||
implementation 'com.google.firebase:firebase-analytics:20.1.0'
|
||||
|
||||
// geeksville-androidlib
|
||||
// compileOnly 'com.google.android.gms:play-services-base:17.6.0'
|
||||
|
||||
// alas implementation bug deep in the bowels when I tried it for my SyncBluetoothDevice class
|
||||
// implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import com.geeksville.mesh.android.Logging
|
|||
import com.geeksville.mesh.android.ServiceClient
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.android.getMissingPermissions
|
||||
import com.geeksville.mesh.android.isGooglePlayAvailable
|
||||
import com.geeksville.mesh.databinding.ActivityMainBinding
|
||||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.BluetoothViewModel
|
||||
|
@ -45,8 +46,6 @@ import com.geeksville.mesh.service.*
|
|||
import com.geeksville.mesh.ui.*
|
||||
import com.geeksville.mesh.util.Exceptions
|
||||
import com.geeksville.mesh.util.exceptionReporter
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
|
@ -252,15 +251,12 @@ class MainActivity : BaseActivity(), Logging {
|
|||
private fun askToRate() {
|
||||
exceptionReporter { // Got one IllegalArgumentException from inside this lib, but we don't want to crash our app because of bugs in this optional feature
|
||||
|
||||
val hasGooglePlay = GoogleApiAvailability.getInstance()
|
||||
.isGooglePlayServicesAvailable(this) != ConnectionResult.SERVICE_MISSING
|
||||
|
||||
val rater = AppRate.with(this)
|
||||
.setInstallDays(10.toByte()) // default is 10, 0 means install day, 10 means app is launched 10 or more days later than installation
|
||||
.setLaunchTimes(10.toByte()) // default is 10, 3 means app is launched 3 or more times
|
||||
.setRemindInterval(1.toByte()) // default is 1, 1 means app is launched 1 or more days after neutral button clicked
|
||||
.setRemindLaunchesNumber(1.toByte()) // default is 0, 1 means app is launched 1 or more times after neutral button clicked
|
||||
.setStoreType(if (hasGooglePlay) StoreType.GOOGLEPLAY else StoreType.AMAZON)
|
||||
.setStoreType(StoreType.GOOGLEPLAY)
|
||||
|
||||
rater.monitor() // Monitors the app launch times
|
||||
|
||||
|
@ -310,7 +306,7 @@ class MainActivity : BaseActivity(), Logging {
|
|||
// Handle any intent
|
||||
handleIntent(intent)
|
||||
|
||||
askToRate()
|
||||
if (isGooglePlayAvailable(this)) askToRate()
|
||||
|
||||
// if (!isInTestLab) - very important - even in test lab we must request permissions because we need location perms for some of our tests to pass
|
||||
requestPermission()
|
||||
|
|
|
@ -10,11 +10,11 @@ import android.provider.Settings
|
|||
import androidx.core.content.edit
|
||||
import com.geeksville.mesh.analytics.AnalyticsProvider
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.android.gms.common.GoogleApiAvailabilityLight
|
||||
|
||||
|
||||
fun isGooglePlayAvailable(context: Context): Boolean {
|
||||
val a = GoogleApiAvailability.getInstance()
|
||||
val a = GoogleApiAvailabilityLight.getInstance()
|
||||
val r = a.isGooglePlayServicesAvailable(context)
|
||||
return r != ConnectionResult.SERVICE_MISSING && r != ConnectionResult.SERVICE_INVALID
|
||||
}
|
||||
|
|
|
@ -1,185 +0,0 @@
|
|||
package com.geeksville.mesh.android
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import com.google.android.gms.common.api.Api
|
||||
import com.google.android.gms.common.api.Api.ApiOptions.NotRequiredOptions
|
||||
import com.google.android.gms.common.api.Scope
|
||||
import com.google.android.gms.common.api.GoogleApiClient
|
||||
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil
|
||||
import android.content.IntentSender
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
|
||||
|
||||
interface PlayClientCallbacks /* : Activity */ {
|
||||
/**
|
||||
* Called to tell activity we've lost connection to play
|
||||
*/
|
||||
fun onPlayConnectionSuspended() :Unit
|
||||
|
||||
/**
|
||||
* Called to tell activity we are now connected to play
|
||||
* Do remaining init here
|
||||
*/
|
||||
fun onPlayConnected() : Unit
|
||||
|
||||
/**
|
||||
* Called when this machine does not have a valid form of play.
|
||||
*/
|
||||
fun onPlayUnavailable() : Unit
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by kevinh on 1/5/15.
|
||||
*/
|
||||
|
||||
public class PlayClient(val context: Activity, val playCallbacks: PlayClientCallbacks) : Logging {
|
||||
|
||||
var apiClient: GoogleApiClient? = null
|
||||
var authInProgress: Boolean = false
|
||||
|
||||
companion object {
|
||||
val PLAY_OAUTH_REQUEST_CODE = 901
|
||||
val AUTH_PENDING = "authPend"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Must be called from onCreate
|
||||
*/
|
||||
fun playOnCreate(savedInstanceState: Bundle?, apis: Array<Api<out NotRequiredOptions>>, scopes: Array<Scope> = arrayOf()) {
|
||||
|
||||
if(savedInstanceState != null)
|
||||
authInProgress = savedInstanceState.getBoolean(AUTH_PENDING)
|
||||
|
||||
if(hasPlayServices()) {
|
||||
var builder = GoogleApiClient.Builder(context)
|
||||
.addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
|
||||
|
||||
override fun onConnected(p0: Bundle?) {
|
||||
// Connected to Google Play services!
|
||||
// The good stuff goes here.
|
||||
|
||||
playCallbacks.onPlayConnected()
|
||||
}
|
||||
|
||||
override fun onConnectionSuspended(i: Int) {
|
||||
// If your connection to the sensor gets lost at some point,
|
||||
// you'll be able to determine the reason and react to it here.
|
||||
if (i == ConnectionCallbacks.CAUSE_NETWORK_LOST) {
|
||||
info("Connection lost. Cause: Network Lost.");
|
||||
} else if (i == ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
|
||||
info("Connection lost. Reason: Service Disconnected");
|
||||
} else
|
||||
errormsg("Unknown play kode $i")
|
||||
|
||||
playCallbacks.onPlayConnectionSuspended()
|
||||
}
|
||||
})
|
||||
.addOnConnectionFailedListener(object : GoogleApiClient.OnConnectionFailedListener {
|
||||
override fun onConnectionFailed(result: ConnectionResult) {
|
||||
info("Play connection failed $result")
|
||||
if (!result.hasResolution()) {
|
||||
showErrorDialog(result.errorCode)
|
||||
} else {
|
||||
// The failure has a resolution. Resolve it.
|
||||
// Called typically when the app is not yet authorized, and an
|
||||
// authorization dialog is displayed to the user.
|
||||
if (!authInProgress) {
|
||||
try {
|
||||
info("Attempting to resolve failed connection");
|
||||
authInProgress = true;
|
||||
result.startResolutionForResult(context,
|
||||
PLAY_OAUTH_REQUEST_CODE);
|
||||
} catch (e: IntentSender.SendIntentException) {
|
||||
errormsg("Exception while starting resolution activity")
|
||||
playCallbacks.onPlayUnavailable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
apis.forEach { api ->
|
||||
builder = builder.addApi(api)
|
||||
}
|
||||
|
||||
scopes.forEach { s ->
|
||||
builder = builder.addScope(s)
|
||||
}
|
||||
|
||||
apiClient = builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorDialog(code: Int) {
|
||||
// Show the localized error dialog
|
||||
GooglePlayServicesUtil.getErrorDialog(code,
|
||||
context, 0)?.show();
|
||||
playCallbacks.onPlayUnavailable()
|
||||
}
|
||||
|
||||
fun hasPlayServices(): Boolean {
|
||||
// Check that Google Play services is available
|
||||
val resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context)
|
||||
// For testing
|
||||
//val resultCode = ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED
|
||||
|
||||
if (ConnectionResult.SUCCESS == resultCode) {
|
||||
// In debug mode, log the status
|
||||
Log.d("Geofence Detection",
|
||||
"Google Play services is available.");
|
||||
|
||||
// getAnalytics().track("Has Play")
|
||||
|
||||
// Continue
|
||||
return true
|
||||
// Google Play services was not available for some reason
|
||||
} else {
|
||||
showErrorDialog(resultCode)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called from onActivityResult
|
||||
* @return true if we handled this
|
||||
*/
|
||||
fun playOnActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean =
|
||||
if (requestCode == PLAY_OAUTH_REQUEST_CODE) {
|
||||
authInProgress = false;
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// Make sure the app is not already connected or attempting to connect
|
||||
if (!apiClient!!.isConnecting && !apiClient!!.isConnected) {
|
||||
apiClient!!.connect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// User opted to not install play
|
||||
errormsg("User declined play")
|
||||
context.finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
else
|
||||
false
|
||||
|
||||
fun playOnStart() {
|
||||
if(apiClient != null)
|
||||
apiClient!!.connect()
|
||||
}
|
||||
|
||||
fun playOnStop() {
|
||||
if(apiClient != null && apiClient!!.isConnected)
|
||||
apiClient!!.disconnect()
|
||||
}
|
||||
|
||||
fun playSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean(AUTH_PENDING, authInProgress)
|
||||
}
|
||||
}
|
|
@ -3,14 +3,11 @@ package com.geeksville.mesh.repository.location
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.os.Looper
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.hasBackgroundPermission
|
||||
import com.google.android.gms.location.LocationCallback
|
||||
import com.google.android.gms.location.LocationRequest
|
||||
import com.google.android.gms.location.LocationResult
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -35,47 +32,43 @@ class SharedLocationManager constructor(
|
|||
val receivingLocationUpdates: StateFlow<Boolean> get() = _receivingLocationUpdates
|
||||
|
||||
// TODO use positionBroadcastSecs / test locationRequest settings
|
||||
private val desiredInterval = 1 * 60 * 1000L
|
||||
// if unset, use positionBroadcastSecs default
|
||||
// positionBroadcastSecs.takeIf { it != 0L }?.times(1000L) ?: (15 * 60 * 1000L)
|
||||
|
||||
// Set up the Fused Location Provider and LocationRequest
|
||||
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
|
||||
private val locationRequest = LocationRequest.create().apply {
|
||||
interval = desiredInterval
|
||||
fastestInterval = 30 * 1000L
|
||||
// smallestDisplacement = 50F // 50 meters
|
||||
priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
|
||||
}
|
||||
private val fastestInterval = 30 * 1000L
|
||||
private val smallestDisplacement = 50F // 50 meters
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private val _locationUpdates = callbackFlow {
|
||||
val callback = object : LocationCallback() {
|
||||
override fun onLocationResult(result: LocationResult) {
|
||||
// info("New location: ${result.lastLocation}")
|
||||
trySend(result.lastLocation)
|
||||
}
|
||||
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
val callback = LocationListener { location ->
|
||||
// info("New location: ${result.lastLocation}")
|
||||
trySend(location)
|
||||
}
|
||||
|
||||
if (!context.hasBackgroundPermission()) close()
|
||||
|
||||
info("Starting location requests with interval=${desiredInterval}ms")
|
||||
|
||||
info("Starting location updates with minTime=${fastestInterval}ms and minDistance=${smallestDisplacement}m")
|
||||
_receivingLocationUpdates.value = true
|
||||
GeeksvilleApplication.analytics.track("location_start") // Figure out how many users needed to use the phone GPS
|
||||
|
||||
fusedLocationClient.requestLocationUpdates(
|
||||
locationRequest,
|
||||
callback,
|
||||
Looper.getMainLooper()
|
||||
).addOnFailureListener { ex ->
|
||||
errormsg("Failed to listen to GPS error: ${ex.message}")
|
||||
close(ex) // in case of exception, close the Flow
|
||||
try {
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER,
|
||||
fastestInterval,
|
||||
smallestDisplacement,
|
||||
callback,
|
||||
context.mainLooper
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
close(e) // in case of exception, close the Flow
|
||||
}
|
||||
|
||||
awaitClose {
|
||||
info("Stopping location requests")
|
||||
_receivingLocationUpdates.value = false
|
||||
GeeksvilleApplication.analytics.track("location_stop")
|
||||
fusedLocationClient.removeLocationUpdates(callback) // clean up when Flow collection ends
|
||||
locationManager.removeUpdates(callback) // clean up when Flow collection ends
|
||||
}
|
||||
}.shareIn(
|
||||
externalScope,
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.core.content.edit
|
|||
import com.geeksville.mesh.analytics.DataPair
|
||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.isGooglePlayAvailable
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.*
|
||||
import com.geeksville.mesh.MeshProtos.MeshPacket
|
||||
|
@ -153,7 +152,7 @@ class MeshService : Service(), Logging {
|
|||
// If we're already observing updates, don't register again
|
||||
if (locationFlow?.isActive == true) return
|
||||
|
||||
if (hasBackgroundPermission() && isGooglePlayAvailable(this)) {
|
||||
if (hasBackgroundPermission()) {
|
||||
locationFlow = locationRepository.getLocations()
|
||||
.onEach { location ->
|
||||
sendPosition(
|
||||
|
|
|
@ -16,7 +16,6 @@ import com.geeksville.mesh.analytics.DataPair
|
|||
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.hideKeyboard
|
||||
import com.geeksville.mesh.android.isGooglePlayAvailable
|
||||
import com.geeksville.mesh.MainActivity
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
|
@ -154,7 +153,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
model.provideLocation.value = false
|
||||
binding.provideLocationCheckbox.isChecked = false
|
||||
} else {
|
||||
binding.provideLocationCheckbox.isEnabled = isGooglePlayAvailable(requireContext())
|
||||
binding.provideLocationCheckbox.isEnabled = true
|
||||
}
|
||||
|
||||
// update the region selection from the device
|
||||
|
@ -582,7 +581,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
scanModel.setupScan()
|
||||
|
||||
// system permissions might have changed while we were away
|
||||
binding.provideLocationCheckbox.isChecked = myActivity.hasBackgroundPermission() && (model.provideLocation.value ?: false) && isGooglePlayAvailable(requireContext())
|
||||
binding.provideLocationCheckbox.isChecked = myActivity.hasBackgroundPermission() && (model.provideLocation.value ?: false)
|
||||
|
||||
myActivity.registerReceiver(updateProgressReceiver, updateProgressFilter)
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ buildscript {
|
|||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
// Add the Crashlytics Gradle plugin.
|
||||
// Check that you have the Google Services Gradle plugin v4.3.2 or later
|
||||
// (if not, add it).
|
||||
// Firebase Crashlytics
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue