org.wso2.iot.agent.services.location.LocationService.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.iot.agent.services.location.LocationService.java

Source

/*
 * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.wso2.iot.agent.services.location;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.support.v4.content.ContextCompat;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.iot.agent.R;
import org.wso2.iot.agent.activities.AlreadyRegisteredActivity;
import org.wso2.iot.agent.events.EventRegistry;
import org.wso2.iot.agent.events.beans.EventPayload;
import org.wso2.iot.agent.events.publisher.HttpDataPublisher;
import org.wso2.iot.agent.utils.CommonUtils;
import org.wso2.iot.agent.utils.Constants;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * This class holds the function implementations of the location service.
 */
public class LocationService extends Service implements LocationListener {

    private static final String TAG = LocationService.class.getSimpleName();

    private LocationManager locationManager = null;
    private Context context;
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 1000; //If more than 1Km
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 5; //If more than 5 minutes
    private List<String> providers = new ArrayList<>();
    private boolean isUpdateRequested = false;
    private final IBinder mBinder = new LocalBinder();
    private long lastPublishedLocationTime;

    public class LocalBinder extends Binder {
        LocationService getService() {
            return LocationService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Starting service with ID: " + startId);
        }
        if (Constants.ASK_TO_ENABLE_LOCATION) {
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                try {
                    int locationSetting = Settings.Secure.getInt(context.getContentResolver(),
                            Settings.Secure.LOCATION_MODE);
                    if (locationSetting == 0) {
                        CommonUtils.displayNotification(context, R.drawable.ic_warning_white_24dp,
                                context.getResources().getString(R.string.title_need_location),
                                context.getResources().getString(R.string.msg_need_location),
                                AlreadyRegisteredActivity.class, Constants.LOCATION_DISABLED,
                                Constants.LOCATION_DISABLED_NOTIFICATION_ID);
                    }
                } catch (Settings.SettingNotFoundException e) {
                    Log.w(TAG, "Location setting is not available on this device");
                }
            } else {
                Log.w(TAG, "Location setting retrieval is not supported by API level "
                        + android.os.Build.VERSION.SDK_INT);
            }
        }
        if (Build.VERSION.SDK_INT >= 23
                && ContextCompat.checkSelfPermission(context,
                        android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(context,
                        android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            displayPermissionMissingNotification();
            return START_NOT_STICKY;
        }
        Location location = null;
        long locationTime = 0;
        //We are trying for all enabled providers
        List<String> _providers = locationManager.getProviders(true);
        for (String p : _providers) {
            Location l = locationManager.getLastKnownLocation(p);
            if (l != null && l.getTime() > locationTime) {
                locationTime = l.getTime();
                location = l;
            } else if (Constants.DEBUG_MODE_ENABLED) {
                Log.d(TAG, "Last known location from provider " + p + " is not found or too old.");
            }
        }
        if (location != null) {
            broadcastLocation(location);
        } else {
            Log.w(TAG, "No last known location found");
        }
        return START_STICKY;
    }

    private void broadcastLocation(Location location) {
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(Constants.LOCATION_UPDATE_BROADCAST_ACTION);
        broadcastIntent.putExtra(Constants.Location.LOCATION, location);
        sendBroadcast(broadcastIntent);
        publishLocationInfo(location);
    }

    @Override
    public void onCreate() {
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Creating service");
        }
        context = getApplicationContext();
        if (locationManager == null) {
            locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
        }
        setLocation();
    }

    @Override
    public void onDestroy() {
        if (locationManager != null && isUpdateRequested) {
            if (Build.VERSION.SDK_INT >= 23
                    && ContextCompat.checkSelfPermission(context,
                            android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    && ContextCompat.checkSelfPermission(context,
                            android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                displayPermissionMissingNotification();
                return;
            }
            try {
                locationManager.removeUpdates(this);
                isUpdateRequested = false;
            } catch (Exception ex) {
                Log.e(TAG, "fail to remove location listeners", ex);
            }
        }
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Service destroyed.");
        }
    }

    /**
     * In this method, it gets the latest location updates from gps/ network.
     */
    private void setLocation() {
        if (locationManager != null) {
            try {
                if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(context,
                        android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                        && ContextCompat.checkSelfPermission(context,
                                android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    displayPermissionMissingNotification();
                    return;
                }
                providers = locationManager.getProviders(true);
                if (providers != null && !providers.isEmpty()) {
                    if (isUpdateRequested) {
                        locationManager.removeUpdates(this);
                    }
                    for (String provider : providers) {
                        if (Constants.DEBUG_MODE_ENABLED) {
                            Log.d(TAG, "Requesting locations from provider: " + provider);
                        }
                        locationManager.requestLocationUpdates(provider, MIN_TIME_BW_UPDATES,
                                MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                    }
                    isUpdateRequested = true;
                } else {
                    Log.w(TAG, "No suitable location providers found");
                }
            } catch (RuntimeException e) {
                Log.e(TAG, "No network or GPS Switched off.", e);
            }
        } else {
            Log.e(TAG, "Location manager is not available.");
            stopSelf();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            if (Constants.DEBUG_MODE_ENABLED) {
                Log.d(TAG, "Location changed> lat:" + location.getLatitude() + " lon:" + location.getLongitude()
                        + " provider:" + location.getProvider());
            }
            broadcastLocation(location);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Status changed to: " + status + " Provider: " + provider);
        }
    }

    @Override
    public void onProviderEnabled(String provider) {
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Provider enabled: " + provider);
        }
        setLocation();
    }

    @Override
    public void onProviderDisabled(String provider) {
        if (Constants.DEBUG_MODE_ENABLED) {
            Log.d(TAG, "Provider disabled: " + provider);
        }
        if (providers.contains(provider) && locationManager != null) {
            setLocation();
        }
    }

    /**
     * To publish the location event to the server
     *
     * @param location location details
     */
    private void publishLocationInfo(Location location) {
        if (!Constants.LOCATION_PUBLISHING_ENABLED) {
            return;
        }
        if (EventRegistry.eventListeningStarted) {
            String locationPayload = getLocationPayload(location);
            if (lastPublishedLocationTime < location.getTime()) {
                EventPayload eventPayload = new EventPayload();
                eventPayload.setPayload(locationPayload);
                eventPayload.setType(Constants.EventListeners.LOCATION_EVENT_TYPE);
                HttpDataPublisher httpDataPublisher = new HttpDataPublisher();
                httpDataPublisher.publish(eventPayload);
                lastPublishedLocationTime = location.getTime();
                if (Constants.DEBUG_MODE_ENABLED) {
                    Log.d(TAG, "Location Event is published.");
                }
            } else {
                if (Constants.DEBUG_MODE_ENABLED) {
                    Log.d(TAG, "Ignore publishing. Duplicate location timestamp.");
                }
            }
        } else {
            Log.w(TAG, "Event listening not started yet");
        }
    }

    /**
     * Returns the location payload.
     *
     * @return - Location info payload as a string
     */
    private String getLocationPayload(Location deviceLocation) {
        String locationString = null;
        double latitude = deviceLocation.getLatitude();
        double longitude = deviceLocation.getLongitude();
        if (latitude != 0 && longitude != 0) {
            JSONObject locationObject = new JSONObject();
            try {
                locationObject.put(Constants.LocationInfo.LATITUDE, latitude);
                locationObject.put(Constants.LocationInfo.LONGITUDE, longitude);
                locationObject.put(Constants.LocationInfo.TIME_STAMP, new Date().getTime());
                locationString = locationObject.toString();
            } catch (JSONException e) {
                Log.e(TAG, "Error occurred while creating a location payload for location event publishing", e);
            }
        }
        return locationString;
    }

    private void displayPermissionMissingNotification() {
        CommonUtils.displayNotification(context, R.drawable.ic_warning_white_24dp,
                context.getResources().getString(R.string.title_need_permissions),
                context.getResources().getString(R.string.msg_need_permissions), AlreadyRegisteredActivity.class,
                Constants.PERMISSION_MISSING, Constants.PERMISSION_MISSING_NOTIFICATION_ID);
    }
}