Add google background service

This commit is contained in:
Mieszko 2018-09-19 21:46:15 +02:00
parent c2a4f13769
commit 62814aaa54
7 changed files with 317 additions and 778 deletions

View File

@ -8,7 +8,7 @@ android {
}
defaultConfig {
applicationId "com.uam.wmi.findmytutor"
minSdkVersion 26
minSdkVersion 23
targetSdkVersion 27
versionCode 1
versionName "1.0"

View File

@ -2,15 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.uam.wmi.findmytutor">
<!-- To auto-complete the email text field in the login form with the user's emails -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- This permission is need to check the internet connection state !-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
@ -42,7 +36,10 @@
android:label="@string/title_activity_login"
android:noHistory="true">
</activity>
<service android:name=".service.LocationService" android:process=":LocationMonitoringService" android:exported="false"/>/>
<service
android:name=".service.GoogleService"
android:exported="false" />
</application>
</manifest>

View File

@ -1,133 +1,115 @@
package com.uam.wmi.findmytutor.activity;
import android.Manifest;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.location.Address;
import android.location.Geocoder;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.uam.wmi.findmytutor.service.GoogleService;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import android.support.design.widget.FloatingActionButton;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.support.annotation.NonNull;
import android.view.MenuItem;
import com.google.android.gms.common.ConnectionResult;
import com.mapbox.mapboxsdk.Mapbox;
import com.uam.wmi.findmytutor.BuildConfig;
import com.mapbox.mapboxsdk.maps.MapView;
import com.uam.wmi.findmytutor.R;
import com.uam.wmi.findmytutor.service.LocationRequestHelper;
import com.uam.wmi.findmytutor.service.LocationResultHelper;
import com.uam.wmi.findmytutor.service.LocationUpdatesBroadcastReceiver;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.uam.wmi.findmytutor.model.Coordinate;
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
// FIXME: 5/16/17
private static final long UPDATE_INTERVAL = 10 * 1000;
/**
* The fastest rate for active location updates. Updates will never be more frequent
* than this value, but they may be less frequent.
*/
// FIXME: 5/14/17
private static final long FASTEST_UPDATE_INTERVAL = UPDATE_INTERVAL / 2;
/**
* The max time before batched results are delivered by location services. Results may be
* delivered sooner than this interval.
*/
private static final long MAX_WAIT_TIME = UPDATE_INTERVAL * 3;
/**
* Stores parameters for requests to the FusedLocationProviderApi.
*/
private LocationRequest mLocationRequest;
/**
* The entry point to Google Play Services.
*/
private GoogleApiClient mGoogleApiClient;
public class MainActivity extends AppCompatActivity {
private BottomNavigationView mMainNav;
private FrameLayout mMainFrame;
private boolean isTutor;
private MapFragment mapFragment;
private NotificationFragment notificationFragment;
private ProfileFragment profileFragment;
// UI Widgets.
private Button mRequestUpdatesButton;
private Button mRemoveUpdatesButton;
private TextView mLocationUpdatesResultView;
private static final int REQUEST_PERMISSIONS = 100;
boolean boolean_permission;
TextView tv_latitude, tv_longitude, tv_address, tv_area, tv_locality;
SharedPreferences mPref;
SharedPreferences.Editor medit;
Double latitude, longitude;
Geocoder geocoder;
private MapView mapView;
public List<Coordinate> getCoordinates() {
return this.coordinates;
}
public void setCoordinates(List<Coordinate> coordinates) {
this.coordinates = coordinates;
}
public List<Coordinate> coordinates;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mRequestUpdatesButton = (Button) findViewById(R.id.request_updates_button);
mRemoveUpdatesButton = (Button) findViewById(R.id.remove_updates_button);
mLocationUpdatesResultView = (TextView) findViewById(R.id.location_updates_result);
Bundle extras = getIntent().getExtras();
mMainFrame = (FrameLayout) findViewById(R.id.main_frame);
mMainNav = (BottomNavigationView) findViewById(R.id.main_nav);
isTutor = (boolean) extras.get("is_tutor");
if (!isTutor) {
mMainNav.findViewById(R.id.nav_profile).setVisibility(View.GONE);
}
mapFragment = new MapFragment();
notificationFragment = new NotificationFragment();
profileFragment = new ProfileFragment();
// Default frag here
//setFragment(mapFragment);
//mMainNav.setSelectedItemId(R.id.nav_map);
setFragment(mapFragment);
mMainNav.setSelectedItemId(R.id.nav_map);
/* code below is resposible for changing colours of tabs in main tab menu */
mMainNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_map:
mMainNav.setItemBackgroundResource(R.color.colorPrimary);
setFragment(mapFragment);
return true;
case R.id.nav_notif:
mMainNav.setItemBackgroundResource(R.color.colorAccent);
setFragment(notificationFragment);
return true;
case R.id.nav_profile:
mMainNav.setItemBackgroundResource(R.color.colorPrimaryDark);
setFragment(profileFragment);
return true;
default:
@ -139,293 +121,129 @@ public class MainActivity extends AppCompatActivity implements GoogleApiClient.
// Logout button
final FloatingActionButton button = findViewById(R.id.logoutButton);
button.setOnClickListener(new View.OnClickListener(){
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SharedPreferences preferences = getSharedPreferences("fmtPrefs",Context.MODE_PRIVATE);
SharedPreferences preferences = getSharedPreferences("fmtPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.clear().commit();
Intent i = getBaseContext().getPackageManager()
.getLaunchIntentForPackage( getBaseContext().getPackageName() );
.getLaunchIntentForPackage(getBaseContext().getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
});
geocoder = new Geocoder(this, Locale.getDefault());
mPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
medit = mPref.edit();
// Check if the user revoked runtime permissions.
if (!checkPermissions()) {
requestPermissions();
if (boolean_permission) {
if (mPref.getString("service", "").matches("")) {
medit.putString("service", "service").commit();
Intent intent = new Intent(getApplicationContext(), GoogleService.class);
startService(intent);
} else {
Toast.makeText(getApplicationContext(), "Service is already running", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(), "Please enable the gps", Toast.LENGTH_SHORT).show();
}
buildGoogleApiClient();
}
private void setFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
fragmentTransaction.commit();
}
@Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onResume() {
super.onResume();
updateButtonsState(LocationRequestHelper.getRequesting(this));
mLocationUpdatesResultView.setText(LocationResultHelper.getSavedLocationResult(this));
registerReceiver(broadcastReceiver, new IntentFilter(GoogleService.str_receiver));
}
@Override
protected void onStop() {
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
super.onStop();
protected void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
}
/**
* Sets up the location request. Android has two location request settings:
* {@code ACCESS_COARSE_LOCATION} and {@code ACCESS_FINE_LOCATION}. These settings control
* the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in
* the AndroidManifest.xml.
* <p/>
* When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update
* interval (5 seconds), the Fused Location Provider API returns location updates that are
* accurate to within a few feet.
* <p/>
* These settings are appropriate for mapping applications that show real-time location
* updates.
*/
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
private void fn_permission() {
if ((ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
mLocationRequest.setInterval(UPDATE_INTERVAL);
if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION))) {
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates faster than this value.
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Sets the maximum time when batched location updates are delivered. Updates may be
// delivered sooner than this interval.
mLocationRequest.setMaxWaitTime(MAX_WAIT_TIME);
}
/**
* Builds {@link GoogleApiClient}, enabling automatic lifecycle management using
* {@link GoogleApiClient.Builder#enableAutoManage(android.support.v4.app.FragmentActivity,
* int, GoogleApiClient.OnConnectionFailedListener)}. I.e., GoogleApiClient connects in
* {@link AppCompatActivity#onStart}, or if onStart() has already happened, it connects
* immediately, and disconnects automatically in {@link AppCompatActivity#onStop}.
*/
private void buildGoogleApiClient() {
if (mGoogleApiClient != null) {
return;
}
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.enableAutoManage(this, this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.i(TAG, "GoogleApiClient connected");
}
private PendingIntent getPendingIntent() {
Intent intent = new Intent(this, LocationUpdatesBroadcastReceiver.class);
intent.setAction(LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES);
return PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
public void onConnectionSuspended(int i) {
final String text = "Connection suspended";
Log.w(TAG, text + ": Error code: " + i);
showSnackbar("Connection suspended");
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
final String text = "Exception while connecting to Google Play services";
Log.w(TAG, text + ": " + connectionResult.getErrorMessage());
showSnackbar(text);
}
/**
* Shows a {@link Snackbar} using {@code text}.
*
* @param text The Snackbar text.
*/
private void showSnackbar(final String text) {
View container = findViewById(R.id.activity_main);
if (container != null) {
Snackbar.make(container, text, Snackbar.LENGTH_LONG).show();
}
}
/**
* Return the current state of the permissions needed.
*/
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION
/**
* Callback received when a permissions request has been completed.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted. Kick off the process of building and connecting
// GoogleApiClient.
buildGoogleApiClient();
},
REQUEST_PERMISSIONS);
}
} else {
// Permission denied.
boolean_permission = true;
}
}
// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.
// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
@Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_PERMISSIONS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
boolean_permission = true;
} else {
Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();
}
}
})
.show();
}
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
latitude = Double.valueOf(intent.getStringExtra("latutide"));
longitude = Double.valueOf(intent.getStringExtra("longitude"));
List<Address> addresses = null;
try {
Log.i(TAG, "Starting location updates");
LocationRequestHelper.setRequesting(this, true);
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, getPendingIntent());
} catch (SecurityException e) {
LocationRequestHelper.setRequesting(this, false);
e.printStackTrace();
}
addresses = geocoder.getFromLocation(latitude, longitude, 1);
String cityName = addresses.get(0).getAddressLine(0);
String stateName = addresses.get(0).getAddressLine(1);
String countryName = addresses.get(0).getAddressLine(2);
tv_area.setText(addresses.get(0).getAdminArea());
tv_locality.setText(stateName);
tv_address.setText(countryName);
} catch (IOException e1) {
e1.printStackTrace();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
if (s.equals(LocationResultHelper.KEY_LOCATION_UPDATES_RESULT)) {
Log.e("Service",LocationResultHelper.getSavedLocationResult(this));
} else if (s.equals(LocationRequestHelper.KEY_LOCATION_UPDATES_REQUESTED)) {
updateButtonsState(LocationRequestHelper.getRequesting(this));
}
}
/**
* Handles the Request Updates button and requests start of location updates.
*/
public void requestLocationUpdates(View view) {
try {
Log.i(TAG, "Starting location updates");
LocationRequestHelper.setRequesting(this, true);
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, getPendingIntent());
} catch (SecurityException e) {
LocationRequestHelper.setRequesting(this, false);
e.printStackTrace();
}
}
tv_latitude.setText(latitude + "");
tv_longitude.setText(longitude + "");
tv_address.getText();
/**
* Handles the Remove Updates button, and requests removal of location updates.
*/
public void removeLocationUpdates(View view) {
Log.i(TAG, "Removing location updates");
LocationRequestHelper.setRequesting(this, false);
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,
getPendingIntent());
}
/**
* Ensures that only one button is enabled at any time. The Start Updates button is enabled
* if the user is not requesting location updates. The Stop Updates button is enabled if the
* user is requesting location updates.
*/
private void updateButtonsState(boolean requestingLocationUpdates) {
}
};
}

View File

@ -0,0 +1,158 @@
package com.uam.wmi.findmytutor.service;
import android.Manifest;
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.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by deepshikha on 24/11/16.
*/
public class GoogleService extends Service implements LocationListener {
boolean isGPSEnable = false;
boolean isNetworkEnable = false;
double latitude, longitude;
LocationManager locationManager;
Location location;
private Handler mHandler = new Handler();
private Timer mTimer = null;
long notify_interval = 5000;
public static String str_receiver = "servicetutorial.service.receiver";
Intent intent;
public GoogleService() {
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mTimer = new Timer();
mTimer.schedule(new TimerTaskToGetLocation(), 5, notify_interval);
intent = new Intent(str_receiver);
}
@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) {
}
private void fn_getlocation() {
locationManager = (LocationManager) getApplicationContext().getSystemService(LOCATION_SERVICE);
isGPSEnable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
isNetworkEnable = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!isGPSEnable && !isNetworkEnable) {
} else {
if (isNetworkEnable) {
location = null;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, this);
if (locationManager!=null){
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location!=null){
Log.e("latitude",location.getLatitude()+"");
Log.e("longitude",location.getLongitude()+"");
latitude = location.getLatitude();
longitude = location.getLongitude();
fn_update(location);
}
}
}
if (isGPSEnable){
location = null;
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000,0,this);
if (locationManager!=null){
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location!=null){
Log.e("latitude",location.getLatitude()+"");
Log.e("longitude",location.getLongitude()+"");
latitude = location.getLatitude();
longitude = location.getLongitude();
fn_update(location);
}
}
}
}
}
private class TimerTaskToGetLocation extends TimerTask{
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
fn_getlocation();
}
});
}
}
private void fn_update(Location location){
intent.putExtra("latutide",location.getLatitude()+"");
intent.putExtra("longitude",location.getLongitude()+"");
sendBroadcast(intent);
}
}

View File

@ -1,397 +0,0 @@
package com.uam.wmi.findmytutor.service;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import static android.content.ContentValues.TAG;
public class LocationService extends Service implements LocationListener, GpsStatus.Listener {
public static final String LOG_TAG = LocationService.class.getSimpleName();
private final LocationServiceBinder binder = new LocationServiceBinder();
boolean isLocationManagerUpdatingLocation;
ArrayList<Location> locationList;
ArrayList<Location> oldLocationList;
ArrayList<Location> noAccuracyLocationList;
ArrayList<Location> inaccurateLocationList;
boolean isLogging;
float currentSpeed = 0.0f; // meters/second
long runStartTimeInMillis;
ArrayList<Integer> batteryLevelArray;
ArrayList<Float> batteryLevelScaledArray;
int batteryScale;
int gpsCount;
public LocationService() {
}
@Override
public void onCreate() {
isLocationManagerUpdatingLocation = false;
locationList = new ArrayList<>();
noAccuracyLocationList = new ArrayList<>();
oldLocationList = new ArrayList<>();
inaccurateLocationList = new ArrayList<>();
isLogging = false;
batteryLevelArray = new ArrayList<>();
batteryLevelScaledArray = new ArrayList<>();
registerReceiver(this.batteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
@Override
public int onStartCommand(Intent i, int flags, int startId) {
super.onStartCommand(i, flags, startId);
return Service.START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onRebind(Intent intent) {
Log.d(LOG_TAG, "onRebind ");
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(LOG_TAG, "onUnbind ");
return true;
}
@Override
public void onDestroy() {
Log.d(LOG_TAG, "onDestroy ");
}
//This is where we detect the app is being killed, thus stop service.
@Override
public void onTaskRemoved(Intent rootIntent) {
Log.d(LOG_TAG, "onTaskRemoved ");
this.stopUpdatingLocation();
stopSelf();
}
/**
* Binder class
*
* @author Takamitsu Mizutori
*
*/
public class LocationServiceBinder extends Binder {
public LocationService getService() {
return LocationService.this;
}
}
/* LocationListener implemenation */
@Override
public void onProviderDisabled(String provider) {
if (provider.equals(LocationManager.GPS_PROVIDER)) {
notifyLocationProviderStatusUpdated(false);
}
}
@Override
public void onProviderEnabled(String provider) {
if (provider.equals(LocationManager.GPS_PROVIDER)) {
notifyLocationProviderStatusUpdated(true);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (provider.equals(LocationManager.GPS_PROVIDER)) {
if (status == LocationProvider.OUT_OF_SERVICE) {
notifyLocationProviderStatusUpdated(false);
} else {
notifyLocationProviderStatusUpdated(true);
}
}
}
/* GpsStatus.Listener implementation */
public void onGpsStatusChanged(int event) {
}
private void notifyLocationProviderStatusUpdated(boolean isLocationProviderAvailable) {
//Broadcast location provider status change here
}
public void startLogging(){
isLogging = true;
}
public void stopLogging(){
if (locationList.size() > 1 && batteryLevelArray.size() > 1){
long currentTimeInMillis = (long)(SystemClock.elapsedRealtimeNanos() / 1000000);
long elapsedTimeInSeconds = (currentTimeInMillis - runStartTimeInMillis) / 1000;
float totalDistanceInMeters = 0;
for(int i = 0; i < locationList.size() - 1; i++){
totalDistanceInMeters += locationList.get(i).distanceTo(locationList.get(i + 1));
}
int batteryLevelStart = batteryLevelArray.get(0).intValue();
int batteryLevelEnd = batteryLevelArray.get(batteryLevelArray.size() - 1).intValue();
float batteryLevelScaledStart = batteryLevelScaledArray.get(0).floatValue();
float batteryLevelScaledEnd = batteryLevelScaledArray.get(batteryLevelScaledArray.size() - 1).floatValue();
saveLog(elapsedTimeInSeconds, totalDistanceInMeters, gpsCount, batteryLevelStart, batteryLevelEnd, batteryLevelScaledStart, batteryLevelScaledEnd);
}
isLogging = false;
}
public void startUpdatingLocation() {
if(this.isLocationManagerUpdatingLocation == false){
isLocationManagerUpdatingLocation = true;
runStartTimeInMillis = (long)(SystemClock.elapsedRealtimeNanos() / 1000000);
locationList.clear();
oldLocationList.clear();
noAccuracyLocationList.clear();
inaccurateLocationList.clear();
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
//Exception thrown when GPS or Network provider were not available on the user's device.
try {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE); //setAccuracyは内部ではhttps://stackoverflow.com/a/17874592/1709287の用にHorizontalAccuracyの設定に変換されている
criteria.setPowerRequirement(Criteria.POWER_HIGH);
criteria.setAltitudeRequired(false);
criteria.setSpeedRequired(true);
criteria.setCostAllowed(true);
criteria.setBearingRequired(false);
//API level 9 and up
criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
//criteria.setBearingAccuracy(Criteria.ACCURACY_HIGH);
//criteria.setSpeedAccuracy(Criteria.ACCURACY_HIGH);
Integer gpsFreqInMillis = 5000;
Integer gpsFreqInDistance = 5; // in meters
locationManager.addGpsStatusListener(this);
locationManager.requestLocationUpdates(gpsFreqInMillis, gpsFreqInDistance, criteria, this, null);
/* Battery Consumption Measurement */
gpsCount = 0;
batteryLevelArray.clear();
batteryLevelScaledArray.clear();
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, e.getLocalizedMessage());
} catch (SecurityException e) {
Log.e(LOG_TAG, e.getLocalizedMessage());
} catch (RuntimeException e) {
Log.e(LOG_TAG, e.getLocalizedMessage());
}
}
}
public void stopUpdatingLocation(){
if(this.isLocationManagerUpdatingLocation == true){
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
locationManager.removeUpdates(this);
isLocationManagerUpdatingLocation = false;
}
}
@Override
public void onLocationChanged(final Location newLocation) {
Log.d(TAG, "(" + newLocation.getLatitude() + "," + newLocation.getLongitude() + ")");
gpsCount++;
if(isLogging){
//locationList.add(newLocation);
filterAndAddLocation(newLocation);
}
Intent intent = new Intent("LocationUpdated");
intent.putExtra("location", newLocation);
LocalBroadcastManager.getInstance(this.getApplication()).sendBroadcast(intent);
}
@SuppressLint("NewApi")
private long getLocationAge(Location newLocation){
long locationAge;
if(android.os.Build.VERSION.SDK_INT >= 17) {
long currentTimeInMilli = (long)(SystemClock.elapsedRealtimeNanos() / 1000000);
long locationTimeInMilli = (long)(newLocation.getElapsedRealtimeNanos() / 1000000);
locationAge = currentTimeInMilli - locationTimeInMilli;
}else{
locationAge = System.currentTimeMillis() - newLocation.getTime();
}
return locationAge;
}
private boolean filterAndAddLocation(Location location){
long age = getLocationAge(location);
if(age > 5 * 1000){ //more than 5 seconds
Log.d(TAG, "Location is old");
oldLocationList.add(location);
return false;
}
if(location.getAccuracy() <= 0){
Log.d(TAG, "Latitidue and longitude values are invalid.");
noAccuracyLocationList.add(location);
return false;
}
//setAccuracy(newLocation.getAccuracy());
float horizontalAccuracy = location.getAccuracy();
if(horizontalAccuracy > 10){ //10meter filter
Log.d(TAG, "Accuracy is too low.");
inaccurateLocationList.add(location);
return false;
}
/* Kalman Filter */
float Qvalue;
long locationTimeInMillis = (long)(location.getElapsedRealtimeNanos() / 1000000);
long elapsedTimeInMillis = locationTimeInMillis - runStartTimeInMillis;
if(currentSpeed == 0.0f){
Qvalue = 3.0f; //3 meters per second
}else{
Qvalue = currentSpeed; // meters per second
}
Location predictedLocation = new Location("");//provider name is unecessary
float predictedDeltaInMeters = predictedLocation.distanceTo(location);
/* Notifiy predicted location to UI */
Intent intent = new Intent("PredictLocation");
intent.putExtra("location", predictedLocation);
LocalBroadcastManager.getInstance(this.getApplication()).sendBroadcast(intent);
Log.d(TAG, "Location quality is good enough.");
currentSpeed = location.getSpeed();
locationList.add(location);
return true;
}
/* Battery Consumption */
private BroadcastReceiver batteryInfoReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context ctxt, Intent intent) {
int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryLevelScaled = batteryLevel / (float)scale;
batteryLevelArray.add(Integer.valueOf(batteryLevel));
batteryLevelScaledArray.add(Float.valueOf(batteryLevelScaled));
batteryScale = scale;
}
};
/* Data Logging */
public synchronized void saveLog(long timeInSeconds, double distanceInMeters, int gpsCount, int batteryLevelStart, int batteryLevelEnd, float batteryLevelScaledStart, float batteryLevelScaledEnd) {
SimpleDateFormat fileNameDateTimeFormat = new SimpleDateFormat("yyyy_MMdd_HHmm");
String filePath = this.getExternalFilesDir(null).getAbsolutePath() + "/"
+ fileNameDateTimeFormat.format(new Date()) + "_battery" + ".csv";
Log.d(TAG, "saving to " + filePath);
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(filePath, false);
fileWriter.append("Time,Distance,GPSCount,BatteryLevelStart,BatteryLevelEnd,BatteryLevelStart(/" + batteryScale + ")," + "BatteryLevelEnd(/" + batteryScale + ")" + "\n");
String record = "" + timeInSeconds + ',' + distanceInMeters + ',' + gpsCount + ',' + batteryLevelStart + ',' + batteryLevelEnd + ',' + batteryLevelScaledStart + ',' + batteryLevelScaledEnd + '\n';
fileWriter.append(record);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}
}

View File

@ -1,17 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">
<RelativeLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">
<android.support.design.widget.FloatingActionButton
android:id="@+id/logoutButton"
android:layout_width="wrap_content"
@ -21,6 +20,7 @@
app:backgroundTint="@android:color/holo_red_dark"
app:srcCompat="@android:drawable/ic_lock_power_off" />
<include layout="@layout/content_main" />
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
@ -29,57 +29,16 @@
</android.support.design.widget.AppBarLayout>
<include
android:id="@+id/include"
layout="@layout/content_main" />
<FrameLayout
android:id="@+id/main_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"></FrameLayout>
<TextView
android:id="@+id/using_batched_location_view"
android:layout_width="330dp"
android:layout_height="65dp"
android:layout_alignParentEnd="true"
android:layout_below="@+id/location_updates_result"
android:layout_marginEnd="13dp"
android:text="@string/batched_location_updates"
android:textSize="@dimen/text_large"
tools:layout_editor_absoluteX="25dp"
tools:layout_editor_absoluteY="106dp" />
<Button
android:id="@+id/request_updates_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="107dp"
android:onClick="requestLocationUpdates"
android:text="@string/request_updates"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="186dp" />
<Button
android:id="@+id/remove_updates_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="165dp"
android:onClick="removeLocationUpdates"
android:text="@string/remove_updates"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="292dp" />
<TextView
android:id="@+id/location_updates_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/default_margin" />
android:layout_height="wrap_content">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="@+id/main_nav"
@ -87,12 +46,15 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_marginStart="0dp"
android:background="@color/colorPrimaryDark"
app:itemBackground="@color/colorPrimaryDark"
app:itemIconTint="@color/nav_item_colors"
app:itemTextColor="@color/nav_item_colors"
app:layout_anchor="@+id/include"
app:layout_anchorGravity="bottom|center"
app:menu="@menu/nav_items"
/>
app:menu="@menu/nav_items" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@ -11,6 +11,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
mapbox:mapbox_renderTextureMode="true"
mapbox:mapbox_cameraTargetLat="52.466799"
mapbox:mapbox_cameraTargetLng="16.927002"
mapbox:mapbox_cameraZoom="17"