Initial build
This commit is contained in:
parent
3ab11592d4
commit
c2a4f13769
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="WizardSettings">
|
|
||||||
<option name="children">
|
|
||||||
<map>
|
|
||||||
<entry key="imageWizard">
|
|
||||||
<value>
|
|
||||||
<PersistentState />
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -8,7 +8,7 @@ android {
|
|||||||
}
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.uam.wmi.findmytutor"
|
applicationId "com.uam.wmi.findmytutor"
|
||||||
minSdkVersion 19
|
minSdkVersion 26
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
@ -45,4 +45,5 @@ dependencies {
|
|||||||
implementation "com.squareup.okhttp3:okhttp:3.11.0"
|
implementation "com.squareup.okhttp3:okhttp:3.11.0"
|
||||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:3.10.0"
|
implementation "com.squareup.okhttp3:okhttp-urlconnection:3.10.0"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:3.11.0"
|
implementation "com.squareup.okhttp3:logging-interceptor:3.11.0"
|
||||||
|
implementation "com.google.android.gms:play-services-location:11.0.4"
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
package="com.uam.wmi.findmytutor">
|
package="com.uam.wmi.findmytutor">
|
||||||
|
|
||||||
<!-- To auto-complete the email text field in the login form with the user's emails -->
|
<!-- To auto-complete the email text field in the login form with the user's emails -->
|
||||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
|
||||||
<uses-permission android:name="android.permission.READ_PROFILE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<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_LOCATION_EXTRA_COMMANDS" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<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" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
@ -42,6 +42,7 @@
|
|||||||
android:label="@string/title_activity_login"
|
android:label="@string/title_activity_login"
|
||||||
android:noHistory="true">
|
android:noHistory="true">
|
||||||
</activity>
|
</activity>
|
||||||
|
<service android:name=".service.LocationService" android:process=":LocationMonitoringService" android:exported="false"/>/>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -1,30 +1,81 @@
|
|||||||
package com.uam.wmi.findmytutor.activity;
|
package com.uam.wmi.findmytutor.activity;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.design.widget.BottomNavigationView;
|
import android.support.design.widget.BottomNavigationView;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
import com.mapbox.mapboxsdk.Mapbox;
|
import com.mapbox.mapboxsdk.Mapbox;
|
||||||
import com.mapbox.mapboxsdk.maps.MapView;
|
import com.uam.wmi.findmytutor.BuildConfig;
|
||||||
import com.uam.wmi.findmytutor.R;
|
import com.uam.wmi.findmytutor.R;
|
||||||
import com.uam.wmi.findmytutor.model.Coordinate;
|
import com.uam.wmi.findmytutor.service.LocationRequestHelper;
|
||||||
import com.uam.wmi.findmytutor.network.ApiClient;
|
import com.uam.wmi.findmytutor.service.LocationResultHelper;
|
||||||
import com.uam.wmi.findmytutor.service.CoordinateService;
|
import com.uam.wmi.findmytutor.service.LocationUpdatesBroadcastReceiver;
|
||||||
|
|
||||||
import java.util.List;
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.location.LocationRequest;
|
||||||
|
import com.google.android.gms.location.LocationServices;
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
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;
|
||||||
|
|
||||||
private BottomNavigationView mMainNav;
|
private BottomNavigationView mMainNav;
|
||||||
private FrameLayout mMainFrame;
|
private FrameLayout mMainFrame;
|
||||||
@ -34,18 +85,10 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private ProfileFragment profileFragment;
|
private ProfileFragment profileFragment;
|
||||||
|
|
||||||
|
|
||||||
|
// UI Widgets.
|
||||||
private MapView mapView;
|
private Button mRequestUpdatesButton;
|
||||||
|
private Button mRemoveUpdatesButton;
|
||||||
public List<Coordinate> getCoordinates() {
|
private TextView mLocationUpdatesResultView;
|
||||||
return this.coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoordinates(List<Coordinate> coordinates) {
|
|
||||||
this.coordinates = coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Coordinate> coordinates;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -54,6 +97,11 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
setContentView(R.layout.activity_main);
|
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);
|
||||||
|
|
||||||
|
|
||||||
mMainFrame = (FrameLayout) findViewById(R.id.main_frame);
|
mMainFrame = (FrameLayout) findViewById(R.id.main_frame);
|
||||||
mMainNav = (BottomNavigationView) findViewById(R.id.main_nav);
|
mMainNav = (BottomNavigationView) findViewById(R.id.main_nav);
|
||||||
|
|
||||||
@ -62,8 +110,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
profileFragment = new ProfileFragment();
|
profileFragment = new ProfileFragment();
|
||||||
|
|
||||||
// Default frag here
|
// Default frag here
|
||||||
setFragment(mapFragment);
|
//setFragment(mapFragment);
|
||||||
mMainNav.setSelectedItemId(R.id.nav_map);
|
//mMainNav.setSelectedItemId(R.id.nav_map);
|
||||||
|
|
||||||
mMainNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
|
mMainNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -106,16 +154,279 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// CoordinateService service = ApiClient.getClient(getApplicationContext())
|
|
||||||
// .create(CoordinateService.class);
|
// Check if the user revoked runtime permissions.
|
||||||
|
if (!checkPermissions()) {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGoogleApiClient();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void setFragment(Fragment fragment) {
|
private void setFragment(Fragment fragment) {
|
||||||
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
|
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
|
||||||
fragmentTransaction.replace(R.id.main_frame, fragment);
|
fragmentTransaction.replace(R.id.main_frame, fragment);
|
||||||
fragmentTransaction.commit();
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
mLocationRequest.setInterval(UPDATE_INTERVAL);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
} else {
|
||||||
|
// Permission denied.
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.uam.wmi.findmytutor.service;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
|
||||||
|
public class LocationRequestHelper {
|
||||||
|
|
||||||
|
public final static String KEY_LOCATION_UPDATES_REQUESTED = "location-updates-requested";
|
||||||
|
|
||||||
|
public static void setRequesting(Context context, boolean value) {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(KEY_LOCATION_UPDATES_REQUESTED, value)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getRequesting(Context context) {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getBoolean(KEY_LOCATION_UPDATES_REQUESTED, false);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,158 @@
|
|||||||
|
package com.uam.wmi.findmytutor.service;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
|
||||||
|
|
||||||
|
import com.uam.wmi.findmytutor.R;
|
||||||
|
import com.uam.wmi.findmytutor.activity.MainActivity;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to process location results.
|
||||||
|
*/
|
||||||
|
public class LocationResultHelper {
|
||||||
|
|
||||||
|
public final static String KEY_LOCATION_UPDATES_RESULT = "location-update-result";
|
||||||
|
|
||||||
|
final private static String PRIMARY_CHANNEL = "default";
|
||||||
|
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private List<Location> mLocations;
|
||||||
|
private NotificationManager mNotificationManager;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
LocationResultHelper(Context context, List<Location> locations) {
|
||||||
|
mContext = context;
|
||||||
|
mLocations = locations;
|
||||||
|
|
||||||
|
NotificationChannel channel = new NotificationChannel(PRIMARY_CHANNEL,
|
||||||
|
context.getString(R.string.default_channel), NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
channel.setLightColor(Color.GREEN);
|
||||||
|
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||||
|
getNotificationManager().createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the title for reporting about a list of {@link Location} objects.
|
||||||
|
*/
|
||||||
|
private String getLocationResultTitle() {
|
||||||
|
String numLocationsReported = mContext.getResources().getQuantityString(
|
||||||
|
R.plurals.num_locations_reported, mLocations.size(), mLocations.size());
|
||||||
|
return numLocationsReported + ": " + DateFormat.getDateTimeInstance().format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLocationResultText() {
|
||||||
|
if (mLocations.isEmpty()) {
|
||||||
|
return mContext.getString(R.string.unknown_location);
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Location location : mLocations) {
|
||||||
|
sb.append("(");
|
||||||
|
sb.append(location.getLatitude());
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(location.getLongitude());
|
||||||
|
sb.append(")");
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves location result as a string to {@link android.content.SharedPreferences}.
|
||||||
|
*/
|
||||||
|
void saveResults() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext)
|
||||||
|
.edit()
|
||||||
|
.putString(KEY_LOCATION_UPDATES_RESULT, getLocationResultTitle() + "\n" +
|
||||||
|
getLocationResultText())
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches location results from {@link android.content.SharedPreferences}.
|
||||||
|
*/
|
||||||
|
public static String getSavedLocationResult(Context context) {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getString(KEY_LOCATION_UPDATES_RESULT, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification mNotificationManager.
|
||||||
|
* <p>
|
||||||
|
* Utility method as this helper works with it a lot.
|
||||||
|
*
|
||||||
|
* @return The system service NotificationManager
|
||||||
|
*/
|
||||||
|
private NotificationManager getNotificationManager() {
|
||||||
|
if (mNotificationManager == null) {
|
||||||
|
mNotificationManager = (NotificationManager) mContext.getSystemService(
|
||||||
|
Context.NOTIFICATION_SERVICE);
|
||||||
|
}
|
||||||
|
return mNotificationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a notification with the location results.
|
||||||
|
*/
|
||||||
|
void showNotification() {
|
||||||
|
Intent notificationIntent = new Intent(mContext, MainActivity.class);
|
||||||
|
|
||||||
|
// Construct a task stack.
|
||||||
|
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
|
||||||
|
|
||||||
|
// Add the main Activity to the task stack as the parent.
|
||||||
|
stackBuilder.addParentStack(MainActivity.class);
|
||||||
|
|
||||||
|
// Push the content Intent onto the stack.
|
||||||
|
stackBuilder.addNextIntent(notificationIntent);
|
||||||
|
|
||||||
|
// Get a PendingIntent containing the entire back stack.
|
||||||
|
PendingIntent notificationPendingIntent =
|
||||||
|
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
|
Notification.Builder notificationBuilder = null;
|
||||||
|
|
||||||
|
notificationBuilder = new Notification.Builder(mContext,
|
||||||
|
PRIMARY_CHANNEL)
|
||||||
|
.setContentTitle(getLocationResultTitle())
|
||||||
|
.setContentText(getLocationResultText())
|
||||||
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(notificationPendingIntent);
|
||||||
|
|
||||||
|
getNotificationManager().notify(0, notificationBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,397 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.uam.wmi.findmytutor.service;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.location.LocationResult;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receiver for handling location updates.
|
||||||
|
*
|
||||||
|
* For apps targeting API level O
|
||||||
|
* {@link android.app.PendingIntent#getBroadcast(Context, int, Intent, int)} should be used when
|
||||||
|
* requesting location updates. Due to limits on background services,
|
||||||
|
* {@link android.app.PendingIntent#getService(Context, int, Intent, int)} should not be used.
|
||||||
|
*
|
||||||
|
* Note: Apps running on "O" devices (regardless of targetSdkVersion) may receive updates
|
||||||
|
* less frequently than the interval specified in the
|
||||||
|
* {@link com.google.android.gms.location.LocationRequest} when the app is no longer in the
|
||||||
|
* foreground.
|
||||||
|
*/
|
||||||
|
public class LocationUpdatesBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
private static final String TAG = "LUBroadcastReceiver";
|
||||||
|
|
||||||
|
public static final String ACTION_PROCESS_UPDATES =
|
||||||
|
"com.google.android.gms.location.sample.backgroundlocationupdates.action" +
|
||||||
|
".PROCESS_UPDATES";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent != null) {
|
||||||
|
final String action = intent.getAction();
|
||||||
|
if (ACTION_PROCESS_UPDATES.equals(action)) {
|
||||||
|
LocationResult result = LocationResult.extractResult(intent);
|
||||||
|
if (result != null) {
|
||||||
|
List<Location> locations = result.getLocations();
|
||||||
|
LocationResultHelper locationResultHelper = new LocationResultHelper(
|
||||||
|
context, locations);
|
||||||
|
// Save the location data to SharedPreferences.
|
||||||
|
locationResultHelper.saveResults();
|
||||||
|
// Show notification with the location data.
|
||||||
|
locationResultHelper.showNotification();
|
||||||
|
Log.i(TAG, LocationResultHelper.getSavedLocationResult(context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
package com.uam.wmi.findmytutor.service;/*
|
||||||
|
* Copyright 2017 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.location.LocationResult;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles incoming location updates and displays a notification with the location data.
|
||||||
|
*
|
||||||
|
* For apps targeting API level 25 ("Nougat") or lower, location updates may be requested
|
||||||
|
* using {@link android.app.PendingIntent#getService(Context, int, Intent, int)} or
|
||||||
|
* {@link android.app.PendingIntent#getBroadcast(Context, int, Intent, int)}. For apps targeting
|
||||||
|
* API level O, only {@code getBroadcast} should be used.
|
||||||
|
*
|
||||||
|
* Note: Apps running on "O" devices (regardless of targetSdkVersion) may receive updates
|
||||||
|
* less frequently than the interval specified in the
|
||||||
|
* {@link com.google.android.gms.location.LocationRequest} when the app is no longer in the
|
||||||
|
* foreground.
|
||||||
|
*/
|
||||||
|
public class LocationUpdatesIntentService extends IntentService {
|
||||||
|
|
||||||
|
static final String ACTION_PROCESS_UPDATES =
|
||||||
|
"com.google.android.gms.location.sample.backgroundlocationupdates.action" +
|
||||||
|
".PROCESS_UPDATES";
|
||||||
|
private static final String TAG = LocationUpdatesIntentService.class.getSimpleName();
|
||||||
|
|
||||||
|
|
||||||
|
public LocationUpdatesIntentService() {
|
||||||
|
// Name the worker thread.
|
||||||
|
super(TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
if (intent != null) {
|
||||||
|
final String action = intent.getAction();
|
||||||
|
if (ACTION_PROCESS_UPDATES.equals(action)) {
|
||||||
|
LocationResult result = LocationResult.extractResult(intent);
|
||||||
|
if (result != null) {
|
||||||
|
List<Location> locations = result.getLocations();
|
||||||
|
LocationResultHelper locationResultHelper = new LocationResultHelper(this,
|
||||||
|
locations);
|
||||||
|
// Save the location data to SharedPreferences.
|
||||||
|
locationResultHelper.saveResults();
|
||||||
|
// Show notification with the location data.
|
||||||
|
locationResultHelper.showNotification();
|
||||||
|
Log.i(TAG, LocationResultHelper.getSavedLocationResult(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -79,6 +79,8 @@
|
|||||||
android:text="@string/action_sign_in"
|
android:text="@string/action_sign_in"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -8,20 +8,10 @@
|
|||||||
tools:context=".activity.MainActivity">
|
tools:context=".activity.MainActivity">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
android:id="@+id/activity_main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
<!--<com.mapbox.mapboxsdk.maps.MapView-->
|
|
||||||
<!--android:id="@+id/mapView"-->
|
|
||||||
<!--android:layout_width="match_parent"-->
|
|
||||||
<!--android:layout_height="match_parent"-->
|
|
||||||
<!--mapbox:mapbox_cameraTargetLat="52.466799"-->
|
|
||||||
<!--mapbox:mapbox_cameraTargetLng="16.927002"-->
|
|
||||||
<!--mapbox:mapbox_cameraZoom="17"-->
|
|
||||||
<!--mapbox:mapbox_styleUrl="mapbox://styles/domagalsky/cjiyzrqjp05l72rmj6ntvv2n8">-->
|
|
||||||
|
|
||||||
<!--</com.mapbox.mapboxsdk.maps.MapView>-->
|
|
||||||
|
|
||||||
<android.support.design.widget.FloatingActionButton
|
<android.support.design.widget.FloatingActionButton
|
||||||
android:id="@+id/logoutButton"
|
android:id="@+id/logoutButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -31,7 +21,6 @@
|
|||||||
app:backgroundTint="@android:color/holo_red_dark"
|
app:backgroundTint="@android:color/holo_red_dark"
|
||||||
app:srcCompat="@android:drawable/ic_lock_power_off" />
|
app:srcCompat="@android:drawable/ic_lock_power_off" />
|
||||||
|
|
||||||
<include layout="@layout/content_main" />
|
|
||||||
|
|
||||||
<android.support.design.widget.AppBarLayout
|
<android.support.design.widget.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -39,16 +28,58 @@
|
|||||||
android:theme="@style/AppTheme.AppBarOverlay">
|
android:theme="@style/AppTheme.AppBarOverlay">
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</android.support.design.widget.AppBarLayout>
|
||||||
<include
|
|
||||||
android:id="@+id/include"
|
|
||||||
layout="@layout/content_main" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/main_frame"
|
android:id="@+id/main_frame"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
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" />
|
||||||
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<android.support.design.widget.BottomNavigationView
|
<android.support.design.widget.BottomNavigationView
|
||||||
android:id="@+id/main_nav"
|
android:id="@+id/main_nav"
|
||||||
@ -64,6 +95,4 @@
|
|||||||
app:menu="@menu/nav_items"
|
app:menu="@menu/nav_items"
|
||||||
/>
|
/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
@ -17,4 +17,6 @@
|
|||||||
<dimen name="dot_text_size">40sp</dimen>
|
<dimen name="dot_text_size">40sp</dimen>
|
||||||
<dimen name="timestamp">14sp</dimen>
|
<dimen name="timestamp">14sp</dimen>
|
||||||
<dimen name="note_list_text">13sp</dimen>
|
<dimen name="note_list_text">13sp</dimen>
|
||||||
|
<dimen name="default_margin">12dp</dimen>
|
||||||
|
<dimen name="text_large">10dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -28,6 +28,24 @@
|
|||||||
<string name="permission_rationale">"Contacts permissions are needed for providing email
|
<string name="permission_rationale">"Contacts permissions are needed for providing email
|
||||||
completions."
|
completions."
|
||||||
</string>
|
</string>
|
||||||
|
<string name="permission_denied_explanation">Permission was denied, but is needed for core
|
||||||
|
functionality.</string>
|
||||||
<string name="title_activity_main" translatable="false">MainActivity</string>
|
<string name="title_activity_main" translatable="false">MainActivity</string>
|
||||||
<string name="access_token" translatable="false">pk.eyJ1IjoiZG9tYWdhbHNreSIsImEiOiJjamd4am4zazYwNXo1MzBxeDZtYjA4d2s4In0.KzNdhc9V_-SYe14AZ-q3Ew</string>
|
<string name="access_token" translatable="false">pk.eyJ1IjoiZG9tYWdhbHNreSIsImEiOiJjamd4am4zazYwNXo1MzBxeDZtYjA4d2s4In0.KzNdhc9V_-SYe14AZ-q3Ew</string>
|
||||||
|
<string name="settings">Settings</string>
|
||||||
|
<string name="ok">OK</string>
|
||||||
|
|
||||||
|
<string name="request_updates">Request updates</string>
|
||||||
|
<string name="remove_updates">Remove updates</string>
|
||||||
|
<string name="unknown_location">Unknown location</string>
|
||||||
|
|
||||||
|
<string name="batched_location_updates">Batched location updates</string>
|
||||||
|
|
||||||
|
<plurals name="num_locations_reported">
|
||||||
|
<item quantity="zero">No location reported</item>
|
||||||
|
<item quantity="one">One location reported</item>
|
||||||
|
<item quantity="other">%d locations reported</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<string name="default_channel">Default channel</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user