at.ac.tuwien.caa.docscan.camera.LocationHandler.java Source code

Java tutorial

Introduction

Here is the source code for at.ac.tuwien.caa.docscan.camera.LocationHandler.java

Source

/*********************************************************************************
 *  DocScan is a Android app for document scanning.
 *
 *  Author:         Fabian Hollaus, Florian Kleber, Markus Diem
 *  Organization:   TU Wien, Computer Vision Lab
 *  Date created:   17. January 2017
 *
 *  This file is part of DocScan.
 *
 *  DocScan is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  DocScan is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with DocScan.  If not, see <http://www.gnu.org/licenses/>.
 *********************************************************************************/

package at.ac.tuwien.caa.docscan.camera;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;

import java.util.List;

/**
 * Created by fabian on 17.01.2017.
 */
public class LocationHandler implements LocationListener {

    private static final int TWO_MINUTES = 1000 * 60 * 2;
    private static final double MIN_ACCURACY = 30;
    private static final long UPDATE_TIME = 100; // Location update time in milli-seconds.
    private static final float UPDATE_DISTANCE = 10; // Location update in meters.
    private static final long MAX_TIME_RUNNING = 1000 * 60 * 5; // Maximum time the location is requested - in milli-seconds. Note: Normally this time should not pass, but a useful location should be found before!

    //    We use here a singleton, because the location should be read one time after the app starts.
    //    The CameraActivity can be created multiple times, during the app is running, so we use here
    //    a static instance to prevent multiple location accesses.
    private static LocationHandler mInstance = null;

    private Location mLocation;
    private LocationManager mLocationManager;
    private Context mContext;
    private long mStartTime;

    public static LocationHandler getInstance(Context context) {

        if (mInstance == null)
            mInstance = new LocationHandler(context);

        return mInstance;

    }

    private LocationHandler(Context context) {

        mContext = context;

        mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);

        LocationListener locationListener = new LocationListener() {
            public void onLocationChanged(Location location) {

                boolean stopManager = false;

                // Called when a new location is found:
                if (isBetterLocation(location, mLocation)) {
                    mLocation = location;

                    if (mLocation.getAccuracy() <= MIN_ACCURACY)
                        stopManager = true;
                }
                if (System.currentTimeMillis() - mStartTime >= MAX_TIME_RUNNING)
                    stopManager = true;

                if (stopManager && ActivityCompat.checkSelfPermission(mContext,
                        Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
                    mLocationManager.removeUpdates(this);

            }

            public void onStatusChanged(String provider, int status, Bundle extras) {
            }

            public void onProviderEnabled(String provider) {
            }

            public void onProviderDisabled(String provider) {
            }
        };

        if (ActivityCompat.checkSelfPermission(mContext,
                Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            List<String> providers = mLocationManager.getProviders(true);
            if (providers.contains(LocationManager.NETWORK_PROVIDER))
                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, UPDATE_TIME,
                        UPDATE_DISTANCE, locationListener);
            if (providers.contains(LocationManager.GPS_PROVIDER))
                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, UPDATE_TIME, UPDATE_DISTANCE,
                        locationListener);

            mStartTime = System.currentTimeMillis();
        }

    }

    public Location getLocation() {

        if (mLocation != null)
            return mLocation;
        else {
            //            If no location has been found yet, use the last known location as a fallback:
            if (ActivityCompat.checkSelfPermission(mContext,
                    Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                Location l1 = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                Location l2 = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

                if (l1 != null && l2 != null) {
                    if (l1.getAccuracy() > l2.getAccuracy())
                        return l1;
                    else
                        return l2;
                } else {
                    if (l1 != null)
                        return l1;
                    else
                        return l2;
                }
            }
        }

        //        If the user has given no permission to access location return nothing:
        return null;
    }

    @Override
    public void onLocationChanged(Location location) {

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    /** Determines whether one Location reading is better than the current Location fix
     * This code is taken from:
     * @see <a href="https://developer.android.com/guide/topics/location/strategies.html">https://developer.android.com/guide/topics/location/strategies.html</a>
     * @param location  The new Location that you want to evaluate
     * @param currentBestLocation  The current Location fix, to which you want to compare the new one
     */
    private boolean isBetterLocation(Location location, Location currentBestLocation) {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return true;
        }

        // Check whether the new location fix is newer or older
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
        boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
        boolean isNewer = timeDelta > 0;

        // If it's been more than two minutes since the current location, use the new location
        // because the user has likely moved
        if (isSignificantlyNewer) {
            return true;
            // If the new location is more than two minutes older, it must be worse
        } else if (isSignificantlyOlder) {
            return false;
        }

        // Check whether the new location fix is more or less accurate
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > 200;

        // Check if the old and new location are from the same provider
        boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());

        // Determine location quality using a combination of timeliness and accuracy
        if (isMoreAccurate) {
            return true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
            return true;
        }
        return false;
    }

    /** Checks whether two providers are the same */
    private boolean isSameProvider(String provider1, String provider2) {
        if (provider1 == null) {
            return provider2 == null;
        }
        return provider1.equals(provider2);
    }

}