У меня было приложение, в котором сотрудник может пробить свою посещаемость через приложение для Android.
Но результат не идеален на 100%. 90% приложение выбирает текущее местоположение и 10% показывает, что местоположение не найдено, и оно случайно, через 1 минуту тот же самый удар сотрудника в приложении, после чего он получает правильный результат
У меня проблемы с получением текущего местоположения пользователя по адресу (геокодеру) в приложении для Android.
После многих поисков по многим ссылкам я нашел следующую ссылку и обнаружил, что JobService лучше всего использовать для определения местоположения и адреса.
Я использовал этот код и интегрировал в соответствии с потребностями моего проекта. Тем не менее, я все еще не получаю местоположение 100%, в то же время, на том же устройстве, тот же пользователь получает местоположение с адресом в приложении Uber / Ola.
Пожалуйста, помогите мне с изменениями или, по крайней мере, помогите мне предоставить полный рабочий код, который будет работать на ВСЕХ УСТРОЙСТВАХ Android, включая ЗАГРУЖЕННЫЕ.
ниже приведен код, который я пробовал
class DashboardActivity : AppCompatActivity() {
var obj: Location? = null
private lateinit var mHandler: IncomingMessageHandler
private lateinit var resultReceiver: AddressResultReceiver
private val REQUEST_CHECK_SETTINGS = 0x1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mHandler = IncomingMessageHandler()
requestPermissionWithDexterForLocation()
}
// check permissions
fun requestPermissionWithDexterForLocation() {
Dexter.withActivity(this@DashboardActivity)
.withPermissions(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
.withListener(object : MultiplePermissionsListener{
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
if (report.areAllPermissionsGranted()) {
Log.i(TAG, "all permission accepted")
checkLocationSettings()
} else if (report.isAnyPermissionPermanentlyDenied) {
// handle denied dialog
} else {
// handle denied dialog
}
}
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken) {
token.continuePermissionRequest()
}
}).check()
}
// check device location settings
private fun checkLocationSettings() {
try {
val builder: LocationSettingsRequest.Builder = LocationSettingsRequest.Builder()
.addLocationRequest(LocationRequest().setInterval(10).setFastestInterval(50).setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY))
.setNeedBle(true)
LocationServices.getSettingsClient(this@DashboardActivity).checkLocationSettings(builder.build())
.addOnSuccessListener {
// call method which starts service to get location
startJobService()
resultReceiver = AddressResultReceiver(Handler())
}
.addOnFailureListener { e ->
val statusCode = (e as ApiException).statusCode
when (statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
Log.i(TAG, "Location settings are not satisfied. Attempting to upgrade " + "location settings ")
try {
val rae = e as ResolvableApiException
rae.startResolutionForResult(this@DashboardActivity, REQUEST_CHECK_SETTINGS)
} catch (sie: IntentSender.SendIntentException) {
Log.i(TAG, "PendingIntent unable to execute request.")
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
val errorMessage = "Location settings are inadequate, and cannot be " + "fixed here. Fix in Settings."
Log.e(TAG, errorMessage)
Toast.makeText(this@DashboardActivity, errorMessage, Toast.LENGTH_SHORT).show()
}
}
}
} catch (e: SecurityException) {
e.printStackTrace()
}
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.i(TAG, "onActivityResult of DashBoardActivity")
if (requestCode == REQUEST_CHECK_SETTINGS) {
Log.i(TAG, "onActivityResult of DashBoardActivity.. RCS")
if (resultCode == Activity.RESULT_OK) {
Log.i(TAG, "onActivityResult of DashBoardActivity.. RCS OK")
requestPermissionWithDexterForLocation()
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this, "App needs location services. Please turn it on and retry.", Toast.LENGTH_LONG).show()
}
}
}
// method containing starting service to get location
fun startJobService() {
MainApp.initializeHud(this@DashboardActivity).show()
val startServiceIntent = Intent(applicationContext, LocationUpdatesService::class.java)
val messengerIncoming = Messenger(mHandler)
startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming)
startService(startServiceIntent)
}
// service handler which contains location result
private inner class IncomingMessageHandler: Handler() {
var TAG: String = "IncomingMessageHandler"
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
when (msg?.what) {
LocationUpdatesService.LOCATION_MESSAGE -> {
// stop service and remove callbacks
mHandler.removeCallbacks { }
val stopIntentService = Intent(applicationContext, LocationUpdatesService::class.java)
stopService(stopIntentService)
if (msg.obj != null) {
obj = msg.obj as Location
val currentDateTimeString: String = DateFormat.getDateTimeInstance().format(Date())
Log.i(TAG, "co-ordinates found")
// call method to start getting address (reverse geocoding) for found co-ordinates
startGeocoderIntentService(obj!!)
} else {
Log.i(TAG, "co-ordinates not found")
val dashboardFragment: Fragment = this@DashboardActivity.adapter.getItem(0)
getCompanyLocation("No location found!", "", "", "", false, Calendar.getInstance())
}
}
}
}
}
// method which starts service to get address (reverse geocoding)
private fun startGeocoderIntentService(location: Location) {
// Create an intent for passing to the intent service responsible for fetching the address.
val intent = Intent(applicationContext, FetchAddressIntentService::class.java).apply {
// Pass the result receiver as an extra to the service.
putExtra(Constants.RECEIVER, resultReceiver)
// Pass the location data as an extra to the service.
putExtra(Constants.LOCATION_DATA_EXTRA, location)
}
// Start the service. If the service isn't already running, it is instantiated and started
// (creating a process for it if needed); if it is running then it remains running. The
// service kills itself automatically once all intents are processed.
startService(intent)
}
// reverse geocoding results we get here
private inner class AddressResultReceiver internal constructor(handler: Handler): ResultReceiver(handler){
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
val c = Calendar.getInstance()
val address = resultData!!.getParcelable<Address>(Constants.RESULT_ADDRESS_KEY)
val dashboardFragment: Fragment = adapter.getItem(0)
if (resultCode == Constants.SUCCESS_RESULT) {
val addressOutput = resultData.getString(Constants.RESULT_DATA_KEY)
val fields = HashMap<String, String>()
val locality: String = if (address.locality == null) "" else address.locality
val address1: String = if (address.getAddressLine(0) == null) "" else address.getAddressLine(0)
fields.put("Location", address1.replace("null,", ""))
} else {
Log.i(TAG, "address not found from intent service")
}
}
}
}
// service which uses class LocationUpdatesComponent to get co-ordinates
public class LocationUpdatesService extends JobService implements LocationUpdatesComponent.ILocationProvider{
private static final String TAG = LocationUpdatesService.class.getSimpleName();
public static final int LOCATION_MESSAGE = 9999;
private Messenger mActivityMessenger;
private LocationUpdatesComponent locationUpdatesComponent;
public LocationUpdatesService() {}
@Override
public boolean onStartJob(JobParameters params) {
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
locationUpdatesComponent.onStop();
return false;
}
@Override
public void onCreate() {
super.onCreate();
locationUpdatesComponent = new LocationUpdatesComponent(this);
locationUpdatesComponent.onCreate(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
mActivityMessenger = intent.getParcelableExtra(DashboardActivity.Companion.getMESSENGER_INTENT_KEY());
}
locationUpdatesComponent.onStart();
return START_STICKY;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
}
@Override
public boolean onUnbind(Intent intent) {
return true;
}
@Override
public void onDestroy() {
Log.i(TAG, "Stopping Location Service");
locationUpdatesComponent.onStop();
stopSelf();
}
@Override
public void onLocationUpdate(Location location) {
sendMessage(location);
}
private void sendMessage(Location location) {
if (mActivityMessenger == null) {
return;
}
Message m = Message.obtain();
m.what = LocationUpdatesService.LOCATION_MESSAGE;
m.obj = location;
try {
mActivityMessenger.send(m);
} catch (RemoteException e) {
Log.e(TAG, "Error passing service object back to activity.");
}
}
}
// class to get location co-ordinates
public class LocationUpdatesComponent {
private static final int TWO_MINUTES = 1000 * 60 * 2;
private static final String TAG = LocationUpdatesComponent.class.getSimpleName();
Context mContext;
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 4 * 1000;
/**
* The fastest rate for active location updates. Updates will never be more frequent
* than this value.
*/
private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
private LocationRequest mLocationRequest;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Callback for changes in location.
*/
private LocationCallback mLocationCallback;
/**
* The current location.
*/
private Location mLocation = null;
public ILocationProvider iLocationProvider;
public LocationUpdatesComponent(ILocationProvider iLocationProvider) {
this.iLocationProvider = iLocationProvider;
}
/**
* create first time to initialize the location components
*
* @param context
*/
public void onCreate(Context context) {
this.mContext = context;
Log.i(TAG, "created...............");
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Log.i(TAG, "onCreate...onLocationResult...............loc " + locationResult.getLastLocation());
mLocation = locationResult.getLastLocation();
// onNewLocation(locationResult.getLastLocation());
}
};
// create location request
createLocationRequest();
// get last known location
// getLastLocation();
}
/**
* start location updates
*/
public void onStart() {
Log.i(TAG, "onStart ");
//hey request for location updates
requestLocationUpdates();
}
/**
* remove location updates
*/
public void onStop() {
Log.i(TAG, "onStop....");
removeLocationUpdates();
}
/**
* Makes a request for location updates. Note that in this sample we merely log the
* {@link SecurityException}.
*/
public void requestLocationUpdates() {
Log.i(TAG, "Requesting location updates");
try {
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.getMainLooper());
checkLocationStatus();
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
}
}
private void checkLocationStatus() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (mLocation != null) {
onNewLocation(mLocation);
} else {
getLastLocation();
}
}
}, 10000);
}
/**
* Removes location updates. Note that in this sample we merely log the
* {@link SecurityException}.
*/
public void removeLocationUpdates() {
Log.i(TAG, "Removing location updates");
try {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
}
}
/**
* get last location
*/
private void getLastLocation() {
try {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
if (mLocation != null) {
onNewLocation(mLocation);
} else if (task.isSuccessful() && task.getResult() != null) {
// Log.i(TAG, "getLastLocation " + mLocation);
onNewLocation(task.getResult());
} else {
Log.w(TAG, "Failed to get location.");
onNewLocation(null);
}
}
});
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission." + unlikely);
}
}
private void onNewLocation(Location location) {
Log.i(TAG, "New location: " + location);
if (this.iLocationProvider != null) {
this.iLocationProvider.onLocationUpdate(location);
}
}
/**
* Sets the location request parameters.
*/
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* implements this interface to get call back of location changes
*/
public interface ILocationProvider {
void onLocationUpdate(Location location);
}
}
// Intent service which executes geocoder api
class FetchAddressIntentService : IntentService("FetchAddress") {
private val TAG = "FetchAddressService"
/**
* The receiver where results are forwarded from this service.
*/
private var receiver: ResultReceiver? = null
/**
* Tries to get the location address using a Geocoder. If successful, sends an address to a
* result receiver. If unsuccessful, sends an error message instead.
* Note: We define a [android.os.ResultReceiver] in * MainActivity to process content
* sent from this service.
*
* This service calls this method from the default worker thread with the intent that started
* the service. When this method returns, the service automatically stops.
*/
override fun onHandleIntent(intent: Intent?) {
var errorMessage = ""
receiver = intent?.getParcelableExtra(Constants.RECEIVER)
// Check if receiver was properly registered.
if (intent == null || receiver == null) {
Log.wtf(TAG, "No receiver received. There is nowhere to send the results.")
return
}
// Get the location passed to this service through an extra.
val location = intent.getParcelableExtra<Location>(Constants.LOCATION_DATA_EXTRA)
// Address found using the Geocoder.
var addresses: List<Address> = emptyList()
// Log.i(TAG, "addresses length: ${addresses.size}")
val emptyAddressOnFailure = Address(Locale.US)
// we are verifying coordinates existence from LocationUpdatesComponents hence we are passing them to the fake address in case geocoding fails
emptyAddressOnFailure.latitude = location.latitude
emptyAddressOnFailure.longitude = location.longitude
// Make sure that the location data was really sent over through an extra. If it wasn't,
// send an error error message and return.
if (location == null) {
errorMessage = getString(R.string.no_location_data_provided)
Log.wtf(TAG, errorMessage)
deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage, emptyAddressOnFailure)
return
}
// Errors could still arise from using the Geocoder (for example, if there is no
// connectivity, or if the Geocoder is given illegal location data). Or, the Geocoder may
// simply not have an address for a location. In all these cases, we communicate with the
// receiver using a resultCode indicating failure. If an address is found, we use a
// resultCode indicating success.
// The Geocoder used in this sample. The Geocoder's responses are localized for the given
// Locale, which represents a specific geographical or linguistic region. Locales are used
// to alter the presentation of information such as numbers or dates to suit the conventions
// in the region they describe.
val geocoder = Geocoder(this, Locale.getDefault())
try {
// Using getFromLocation() returns an array of Addresses for the area immediately
// surrounding the given latitude and longitude. The results are a best guess and are
// not guaranteed to be accurate.
addresses = geocoder.getFromLocation(
location.latitude,
location.longitude,
// In this sample, we get just a single address.
1)
} catch (ioException: IOException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available)
// Log.e(TAG, errorMessage, ioException)
} catch (illegalArgumentException: IllegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used)
// Log.e(TAG, "$errorMessage. Latitude = $location.latitude , " +
// "Longitude = $location.longitude", illegalArgumentException)
}
// Log.i(TAG, "addresses length: ${addresses.size}")
// Handle case where no address was found.
if (addresses.isEmpty()) {
if (errorMessage.isEmpty()) {
errorMessage = getString(R.string.no_address_found)
Log.e(TAG, errorMessage)
}
deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage, emptyAddressOnFailure)
} else {
val address = addresses[0]
// Fetch the address lines using {@code getAddressLine},
// join them, and send them to the thread. The {@link android.location.address}
// class provides other options for fetching address details that you may prefer
// to use. Here are some examples:
// getLocality() ("Mountain View", for example)
// getAdminArea() ("CA", for example)
// getPostalCode() ("94043", for example)
// getCountryCode() ("US", for example)
// getCountryName() ("United States", for example)
val addressFragments = with(address) {
(0..maxAddressLineIndex).map { getAddressLine(it) }
}
Log.i(TAG, getString(R.string.address_found))
deliverResultToReceiver(Constants.SUCCESS_RESULT,
addressFragments.joinToString(separator = "\n"), address)
}
}
/**
* Sends a resultCode and message to the receiver.
*/
private fun deliverResultToReceiver(resultCode: Int, message: String, address: Address) {
val bundle = Bundle().apply {
putString(Constants.RESULT_DATA_KEY, message)
putParcelable(Constants.RESULT_ADDRESS_KEY, address)
}
receiver?.send(resultCode, bundle)
stopSelf()
}
}