Не удается найти местоположение каждый раз в Android SDK - PullRequest
0 голосов
/ 19 июня 2019

У меня было приложение, в котором сотрудник может пробить свою посещаемость через приложение для 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()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...