/** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, * copy, modify, and distribute this software in source code or binary form for use * in connection with the web services and APIs provided by Facebook. * * As with any software that integrates with the Facebook platform, your use of * this software is subject to the Facebook Developer Principles and Policies * [http://developers.facebook.com/policy/]. This copyright notice shall be * included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.facebook.places.internal; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.HandlerThread; import com.facebook.internal.Validate; import java.util.ArrayList; import java.util.List; /** * com.facebook.places.internal is solely for the use of other packages within the * Facebook SDK for Android. Use of any of the classes in this package is * unsupported, and they may be modified or removed without warning at any time. */ @SuppressWarnings("MissingPermission") public class LocationScannerImpl implements LocationScanner, LocationListener { private static final long MIN_TIME_BETWEEN_UPDATES = 100L; private static final float MIN_DISTANCE_BETWEEN_UPDATES = 0f; private Context context; private LocationManager locationManager; private LocationPackageRequestParams params; private Location freshLocation; private final Object scanLock = new Object(); private List<String> enabledProviders; public LocationScannerImpl( Context context, LocationPackageRequestParams params) { this.context = context; this.params = params; locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } @Override public void initAndCheckEligibility() throws ScannerException { if (!Validate.hasLocationPermission(context)) { throw new ScannerException(ScannerException.Type.PERMISSION_DENIED); } enabledProviders = new ArrayList<>(params.getLocationProviders().length); for (String provider : params.getLocationProviders()) { if (locationManager.isProviderEnabled(provider)) { enabledProviders.add(provider); } } if (enabledProviders.isEmpty()) { throw new ScannerException(ScannerException.Type.DISABLED); } } private Location getLastLocation(String provider) { Location lastLocation = locationManager.getLastKnownLocation(provider); if (lastLocation != null) { long lastLocationTs = lastLocation.getTime(); long locationAgeMs = System.currentTimeMillis() - lastLocationTs; if (locationAgeMs < params.getLastLocationMaxAgeMs()) { return lastLocation; } } return null; } @Override public Location getLocation() throws ScannerException { for (String provider : enabledProviders) { Location lastLocation = getLastLocation(provider); if (lastLocation != null) { return lastLocation; } } return getFreshLocation(); } private Location getFreshLocation() throws ScannerException { freshLocation = null; HandlerThread handlerThread = new HandlerThread("LocationScanner"); try { handlerThread.start(); for (String provider : enabledProviders) { locationManager.requestLocationUpdates( provider, MIN_TIME_BETWEEN_UPDATES, MIN_DISTANCE_BETWEEN_UPDATES, this, handlerThread.getLooper()); } try { synchronized (scanLock) { scanLock.wait(params.getLocationRequestTimeoutMs()); } } catch (Exception e) { // ignore } } finally { locationManager.removeUpdates(this); handlerThread.quit(); } if (freshLocation == null) { throw new ScannerException(ScannerException.Type.TIMEOUT); } return freshLocation; } @Override public void onLocationChanged(Location location) { if (freshLocation == null) { if (location.getAccuracy() < params.getLocationMaxAccuracyMeters()) { synchronized (scanLock) { freshLocation = location; scanLock.notify(); } } } } @Override public void onStatusChanged(String provider, int status, Bundle extras) { // ignore } @Override public void onProviderEnabled(String provider) { // ignore } @Override public void onProviderDisabled(String provider) { // ignore } }