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.
+ *
+ * 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());
+ }
+}
diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/LocationService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/LocationService.java
new file mode 100644
index 0000000..566ae34
--- /dev/null
+++ b/app/src/main/java/com/uam/wmi/findmytutor/service/LocationService.java
@@ -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 locationList;
+
+ ArrayList oldLocationList;
+ ArrayList noAccuracyLocationList;
+ ArrayList inaccurateLocationList;
+
+
+ boolean isLogging;
+
+ float currentSpeed = 0.0f; // meters/second
+ long runStartTimeInMillis;
+
+ ArrayList batteryLevelArray;
+ ArrayList 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();
+ }
+ }
+ }
+ }
+
+
+
+}
+
+
diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/LocationUpdatesBroadcastReceiver.java b/app/src/main/java/com/uam/wmi/findmytutor/service/LocationUpdatesBroadcastReceiver.java
new file mode 100644
index 0000000..9e8a0dc
--- /dev/null
+++ b/app/src/main/java/com/uam/wmi/findmytutor/service/LocationUpdatesBroadcastReceiver.java
@@ -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 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));
+ }
+ }
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/uam/wmi/findmytutor/service/LocationUpdatesIntentService.java b/app/src/main/java/com/uam/wmi/findmytutor/service/LocationUpdatesIntentService.java
new file mode 100644
index 0000000..95c69d6
--- /dev/null
+++ b/app/src/main/java/com/uam/wmi/findmytutor/service/LocationUpdatesIntentService.java
@@ -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 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));
+ }
+ }
+ }
+ }
+}
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index c2c77b7..70a3e8d 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -79,6 +79,8 @@
android:text="@string/action_sign_in"
android:textStyle="bold" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index b1e333b..37fcb88 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -8,20 +8,10 @@
tools:context=".activity.MainActivity">
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content"
+ >
-
-
+
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+
+
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 93fc64f..bcc07bd 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -17,4 +17,6 @@
40sp
14sp
13sp
+ 12dp
+ 10dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6464e50..6569f70 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -28,6 +28,24 @@
"Contacts permissions are needed for providing email
completions."
+ Permission was denied, but is needed for core
+functionality.
MainActivity
pk.eyJ1IjoiZG9tYWdhbHNreSIsImEiOiJjamd4am4zazYwNXo1MzBxeDZtYjA4d2s4In0.KzNdhc9V_-SYe14AZ-q3Ew
+ Settings
+ OK
+
+ Request updates
+ Remove updates
+ Unknown location
+
+ Batched location updates
+
+
+ - No location reported
+ - One location reported
+ - %d locations reported
+
+
+ Default channel
diff --git a/build.gradle b/build.gradle
index 077cb2f..7eb8ea1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
-
+
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files