diff --git a/app/build.gradle b/app/build.gradle
index 177acbe..fa33149 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -37,6 +37,14 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
+ //Google Maps SDK
+ implementation 'com.google.android.gms:play-services-maps:19.1.0'
+
+ // Glide
+ implementation 'com.github.bumptech.glide:glide:4.16.0' // Check for the latest version
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
+
+
//Firebase Dependencies
implementation platform('com.google.firebase:firebase-bom:33.10.0')
implementation 'com.google.firebase:firebase-analytics'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d2d6e8a..533cec3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,10 @@
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
+
+
@@ -29,11 +33,16 @@
-
+
-
+
+
+
@@ -43,7 +52,8 @@
-
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/DoerflingerHelpersDeviceAdminReceiver.java b/app/src/main/java/com/aldo/apps/familyhelpers/DoerflingerHelpersDeviceAdminReceiver.java
index 9c2db80..114981b 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/DoerflingerHelpersDeviceAdminReceiver.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/DoerflingerHelpersDeviceAdminReceiver.java
@@ -4,14 +4,16 @@ import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;
+import androidx.annotation.NonNull;
+
public class DoerflingerHelpersDeviceAdminReceiver extends DeviceAdminReceiver {
@Override
- public void onEnabled(final Context context,final Intent intent) {
+ public void onEnabled(@NonNull final Context context, @NonNull final Intent intent) {
// Called when the app is enabled as a device administrator.
}
@Override
- public void onDisabled(final Context context, final Intent intent) {
+ public void onDisabled(@NonNull final Context context, @NonNull final Intent intent) {
// Called when the app is disabled as a device administrator.
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java
index 4b00439..26780f2 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java
@@ -162,15 +162,16 @@ public class HelperGridActivity extends AppCompatActivity {
if (mLocationHelper.requestLocationPermissions(HelperGridActivity.this)) {
Log.d(TAG, "launchHelper: Permission already granted");
mLocationHelper.toggleUpdate(HelperGridActivity.this);
+ final Intent intent = new Intent(HelperGridActivity.this, ShareLocationActivity.class);
+ startActivity(intent);
}
}
};
mShareLocationTile.setLogo(R.drawable.ic_location_helper);
mShareLocationTile.setTitle(R.string.title_share_location);
+
}
-
-
/**
* Helper method to request the NotificationPermission as it is required for the service to run.
*
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/ShareLocationActivity.java b/app/src/main/java/com/aldo/apps/familyhelpers/ShareLocationActivity.java
new file mode 100644
index 0000000..2a0893c
--- /dev/null
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/ShareLocationActivity.java
@@ -0,0 +1,215 @@
+package com.aldo.apps.familyhelpers;
+
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.METER_PER_SECOND_TO_KMH_CONVERTER;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.aldo.apps.familyhelpers.model.LocationObject;
+import com.aldo.apps.familyhelpers.workers.DatabaseHelper;
+import com.bumptech.glide.Glide;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MapStyleOptions;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import io.reactivex.rxjava3.disposables.Disposable;
+
+/**
+ * Activity showing a Map to display the shared location plus additional information.
+ */
+public class ShareLocationActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ /**
+ * Tag for debugging purpose.
+ */
+ private static final String TAG = "ShareLocationActivity";
+
+ /**
+ * The {@link GoogleMap} view.
+ */
+ private GoogleMap mGmap;
+
+ /**
+ * The {@link DatabaseHelper} to read and load data from.
+ */
+ private DatabaseHelper mDbHelper;
+
+ /**
+ * The currently logged in {@link FirebaseUser}.
+ */
+ private FirebaseUser mCurrentUser;
+
+ /**
+ * The ID of the location updates to listen to.
+ */
+ private String mShareId;
+
+ /**
+ * The InfoBox {@link ImageView} showing the user icon.
+ */
+ private ImageView mInfoBoxIcon;
+
+ /**
+ * The InfoBox {@link TextView} showing the title.
+ */
+ private TextView mInfoBoxTitle;
+
+ /**
+ * The InfoBox {@link TextView} showing the Location information.
+ */
+ private TextView mInfoBoxLocation;
+
+ /**
+ * The InfoBox {@link TextView} showing the altitude..
+ */
+ private TextView mInfoBoxAltitude;
+
+ /**
+ * The InfoBox {@link TextView} showing the last received speed.
+ */
+ private TextView mInfoBoxSpeed;
+
+ /**
+ * The InfoBox {@link TextView} showing the timestamp of the last update.
+ */
+ private TextView mInfoBoxTimeStamp;
+
+ /**
+ * The {@link Disposable} holding the subscription to the {@link LocationObject}
+ * {@link io.reactivex.rxjava3.subjects.BehaviorSubject} in order to have it cancellable.
+ */
+ private Disposable mLocationUpdateSubscription;
+
+ /**
+ * Boolean flag indicating whether the system is currently in night mode or not.
+ */
+ private boolean mIsNightMode;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_share_location);
+
+ final SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.map);
+ mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
+ mInfoBoxIcon = findViewById(R.id.share_location_info_user_icon);
+ Glide.with(this)
+ .load(mCurrentUser.getPhotoUrl())
+ .into(mInfoBoxIcon);
+ mInfoBoxTitle = findViewById(R.id.share_location_info_title);
+ mInfoBoxLocation = findViewById(R.id.share_location_info_location);
+ mInfoBoxAltitude = findViewById(R.id.share_location_info_altitude);
+ mInfoBoxSpeed = findViewById(R.id.share_location_info_speed);
+ mInfoBoxTimeStamp = findViewById(R.id.share_location_info_timestamp);
+ mapFragment.getMapAsync(this);
+ mDbHelper = new DatabaseHelper();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ final Configuration configuration = getResources().getConfiguration();
+
+ mIsNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+
+ if (mShareId == null) {
+ mShareId = mCurrentUser.getUid();
+ }
+ mDbHelper.startListeningForLocationUpdates(mShareId);
+ if (mLocationUpdateSubscription != null) {
+ mLocationUpdateSubscription.dispose();
+ }
+ mLocationUpdateSubscription = mDbHelper.getLocationSubject()
+ .subscribe(this::handleReceivedLocation, this::handleSubscriptionError);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mLocationUpdateSubscription != null) {
+ mLocationUpdateSubscription.dispose();
+ }
+ }
+
+ /**
+ * Helper method to handle the received location by updating both the Map and the Info box.
+ *
+ * @param locationObject The received {@link LocationObject} holding the new information.
+ */
+ private void handleReceivedLocation(final LocationObject locationObject) {
+ Log.d(TAG, "handleReceivedLocation() called with: locationObject = [" + locationObject + "]");
+ mInfoBoxTitle.setText(String.format(getString(R.string.share_location_info_title), mCurrentUser.getDisplayName()));
+ mInfoBoxLocation.setText(String.format(getString(R.string.share_location_info_location),
+ locationObject.getLatitude(), locationObject.getLongitude()));
+ mInfoBoxAltitude.setText(String.format(getString(R.string.share_location_info_altitude),
+ locationObject.getAltitude()));
+ mInfoBoxSpeed.setText(String.format(getString(R.string.share_location_info_speed),
+ locationObject.getSpeed(),
+ locationObject.getSpeed() * METER_PER_SECOND_TO_KMH_CONVERTER));
+ mInfoBoxTimeStamp.setText(String.format(getString(R.string.share_location_info_timestamp), formatTime(locationObject.getTimestamp())));
+
+ final LatLng received = new LatLng(locationObject.getLatitude(), locationObject.getLongitude());
+
+ mGmap.addMarker(new MarkerOptions()
+ .position(received)
+ .title(mCurrentUser.getDisplayName()));
+ mGmap.moveCamera(CameraUpdateFactory.newLatLngZoom(received, 15.0f));
+
+ }
+
+ /**
+ * Handle potential errors on the subscription to the Location.
+ *
+ * @param error The error that occurred.
+ */
+ private void handleSubscriptionError(final Throwable error) {
+ Log.e(TAG, "handleSubscriptionError: ", error);
+ }
+
+ /**
+ * Formats the provided time in milliseconds in a human readable format.
+ *
+ * @param millis The remaining time in milliseconds.
+ *
+ * @return The String representation of the milliseconds.
+ */
+ private String formatTime(final long millis) {
+ final Date date = new Date(millis);
+ final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy - HH:mm:ss", Locale.getDefault());
+ return simpleDateFormat.format(date);
+ }
+
+ @Override
+ public void onMapReady(@NonNull final GoogleMap googleMap) {
+ mGmap = googleMap;
+ if (mIsNightMode) {
+ mGmap.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.map_style_night));
+ } else {
+ mGmap.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.map_style_day));
+ }
+ final LatLng defaultHome = new LatLng(48.41965746149261, 9.909289365473684);
+
+ mGmap.addMarker(new MarkerOptions().position(defaultHome).title("Default - Home"));
+ mGmap.moveCamera(CameraUpdateFactory.newLatLngZoom(defaultHome, 15.0f));
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/ui/HelperGroupTile.java b/app/src/main/java/com/aldo/apps/familyhelpers/ui/HelperGroupTile.java
index 2b176b9..a3bbb31 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/ui/HelperGroupTile.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/ui/HelperGroupTile.java
@@ -38,7 +38,7 @@ public abstract class HelperGroupTile implements View.OnClickListener {
/**
* The {@link WeakReference} of the {@link Context} from where this was instantiated.
*/
- private WeakReference mContextRef;
+ private final WeakReference mContextRef;
/**
* The name of the helper, mainly used for logging purpose.
@@ -50,7 +50,7 @@ public abstract class HelperGroupTile implements View.OnClickListener {
*
* @param rootLayout The previously inflated view of the root layout.
*/
- public HelperGroupTile(final View rootLayout) {
+ protected HelperGroupTile(final View rootLayout) {
mContextRef = new WeakReference<>(rootLayout.getContext());
mHelperLogo = rootLayout.findViewById(R.id.iv_helper_group_icon);
mHelperTitle = rootLayout.findViewById(R.id.tv_helper_group_title);
@@ -77,7 +77,7 @@ public abstract class HelperGroupTile implements View.OnClickListener {
if (mHelperTitle != null) {
mHelperTitle.setText(titleId);
} else {
- Log.d(TAG, "setLogo: Cannot set Logo for [" + mHelperName + "]");
+ Log.d(TAG, "setTitle: Cannot set Title for [" + mHelperName + "]");
}
}
@@ -91,7 +91,7 @@ public abstract class HelperGroupTile implements View.OnClickListener {
if (mHelperTitle != null) {
mHelperTitle.setText(groupTitle);
} else {
- Log.d(TAG, "setLogo: Cannot set Logo for [" + mHelperName + "]");
+ Log.d(TAG, "setTitleString: Cannot set TitleId for [" + mHelperName + "]");
}
}
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java b/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java
index 6d4c210..28bb884 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java
@@ -73,6 +73,8 @@ public final class GlobalConstants {
*/
public static final int DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS = 5;
+ public static final double METER_PER_SECOND_TO_KMH_CONVERTER = 3.6;
+
/**
* List of available Firebase signIn/Login providers.
*/
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/workers/DatabaseHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/workers/DatabaseHelper.java
index f94aea2..040d92e 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/workers/DatabaseHelper.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/DatabaseHelper.java
@@ -6,12 +6,12 @@ import static com.aldo.apps.familyhelpers.utils.DatabaseConstants.DB_DOC_USER_ID
import android.util.Log;
-
import com.aldo.apps.familyhelpers.model.LocationObject;
import com.aldo.apps.familyhelpers.model.User;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.firestore.FirebaseFirestore;
+import com.google.firebase.firestore.ListenerRegistration;
import java.util.ArrayList;
import java.util.List;
@@ -43,6 +43,13 @@ public class DatabaseHelper {
*/
private final BehaviorSubject> mAllUsers = BehaviorSubject.create();
+ /**
+ * Local {@link BehaviorSubject} representation holding the {@link LocationObject} that is to be observed.
+ */
+ private BehaviorSubject mObservedLocation = BehaviorSubject.create();
+
+ private ListenerRegistration mLocationUpdateListener;
+
/**
* C'tor.
*/
@@ -84,6 +91,24 @@ public class DatabaseHelper {
});
}
+ /**
+ * Returns the {@link BehaviorSubject} containing the {@link List} of all {@link User} objects.
+ *
+ * @return The {@link BehaviorSubject} containing the {@link List} of all {@link User} objects.
+ */
+ public BehaviorSubject> getAllUsers() {
+ return mAllUsers;
+ }
+
+ /**
+ * Returns the {@link BehaviorSubject} containing the {@link LocationObject} to be observed.
+ *
+ * @return The {@link BehaviorSubject} containing the {@link LocationObject} to be observed.
+ */
+ public BehaviorSubject getLocationSubject() {
+ return mObservedLocation;
+ }
+
/**
* Helper method to insert or update a {@link LocationObject} in the database.
*
@@ -95,6 +120,30 @@ public class DatabaseHelper {
.set(locationObject);
}
+ /**
+ * Start listening and publishing {@link LocationObject} updates on {@link #mObservedLocation}.
+ *
+ * @param shareId The ID to be observed.
+ */
+ public void startListeningForLocationUpdates(final String shareId) {
+ mObservedLocation = BehaviorSubject.create();
+ mLocationUpdateListener = mDatabase.collection(DB_COLL_LOCATION)
+ .document(shareId)
+ .addSnapshotListener((value, error) -> {
+ if (value == null) {
+ Log.d(TAG, "onEvent: Location was deleted");
+ mObservedLocation.onComplete();
+ return;
+ }
+ final LocationObject updatedLocation = value.toObject(LocationObject.class);
+ if (updatedLocation == null) {
+ Log.w(TAG, "onEvent: Error while parsing, ignore");
+ return;
+ }
+ mObservedLocation.onNext(updatedLocation);
+ });
+ }
+
/**
* Cancels (by deleting) the ongoing {@link LocationObject} sharing in the database.
*/
@@ -103,6 +152,9 @@ public class DatabaseHelper {
Log.w(TAG, "cancelOngoingLocationSharing: No user logged in, cannot stop sharing");
return;
}
+ if (mLocationUpdateListener != null) {
+ mLocationUpdateListener.remove();
+ }
mDatabase.collection(DB_COLL_LOCATION).document(mCurrentUser.getUid()).delete();
}
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java
index a862fe9..6551e4a 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java
@@ -67,17 +67,17 @@ public final class LocationHelper implements LocationListener {
* The {@link BehaviorSubject} for the {@link LocationObject} for clients to subscribe to.
* TODO: Check if needed.
*/
- private BehaviorSubject mLocationSubject = BehaviorSubject.create();
+ private final BehaviorSubject mLocationSubject = BehaviorSubject.create();
/**
* The {@link DatabaseHelper} for db access.
*/
- private DatabaseHelper mDbHelper = new DatabaseHelper();
+ private final DatabaseHelper mDbHelper = new DatabaseHelper();
/**
* The {@link NotificationHelper} to show and update {@link Notification}s.
*/
- private NotificationHelper mNotificationHelper;
+ private final NotificationHelper mNotificationHelper;
/**
* Private C'Tor for singleton instance.
@@ -93,10 +93,10 @@ public final class LocationHelper implements LocationListener {
}
/**
- * Returns the singleton instance of thie {@link LocationHelper}.
+ * Returns the singleton instance of this {@link LocationHelper}.
*
* @param context The {@link Context) from where this was instantiated.
- * @return The singleton instance of thie {@link LocationHelper}.
+ * @return The singleton instance of this {@link LocationHelper}.
*/
public static LocationHelper getInstance(final Context context) {
if (sInstance == null) {
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/workers/SleepTimerHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/workers/SleepTimerHelper.java
index b6ec9d4..aea0cc5 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/workers/SleepTimerHelper.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/SleepTimerHelper.java
@@ -8,12 +8,9 @@ import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SLEEP_TIMER_DURA
import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SLEEP_TIMER_NOTIFICATION_ID;
import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
-import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
diff --git a/app/src/main/res/drawable/ic_current_location.xml b/app/src/main/res/drawable/ic_current_location.xml
new file mode 100644
index 0000000..a7911aa
--- /dev/null
+++ b/app/src/main/res/drawable/ic_current_location.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_share_location.xml b/app/src/main/res/layout/activity_share_location.xml
new file mode 100644
index 0000000..ec59725
--- /dev/null
+++ b/app/src/main/res/layout/activity_share_location.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/raw/map_style_day.json b/app/src/main/res/raw/map_style_day.json
new file mode 100644
index 0000000..5d3963d
--- /dev/null
+++ b/app/src/main/res/raw/map_style_day.json
@@ -0,0 +1,46 @@
+[
+ {
+ "featureType": "administrative.land_parcel",
+ "elementType": "labels",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.business",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "road.local",
+ "elementType": "labels",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/app/src/main/res/raw/map_style_night.json b/app/src/main/res/raw/map_style_night.json
new file mode 100644
index 0000000..29769c6
--- /dev/null
+++ b/app/src/main/res/raw/map_style_night.json
@@ -0,0 +1,205 @@
+[
+ {
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#242f3e"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#746855"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#242f3e"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.land_parcel",
+ "elementType": "labels",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.locality",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.business",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#263c3f"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#6b9a76"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#38414e"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry.stroke",
+ "stylers": [
+ {
+ "color": "#212a37"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#9ca5b3"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#746855"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry.stroke",
+ "stylers": [
+ {
+ "color": "#1f2835"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#f3d19c"
+ }
+ ]
+ },
+ {
+ "featureType": "road.local",
+ "elementType": "labels",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "transit",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#2f3948"
+ }
+ ]
+ },
+ {
+ "featureType": "transit.station",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#d59563"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#17263c"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#515c6d"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#17263c"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5d901ee..a42fd49 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -19,9 +19,14 @@
%02d:%02d:%02d"
%02d:%02d"
-
+
Share Location
ShareLocation
You are sharing your current location within the family
+ You are following %s
+ Last Received Location = %f;%f
+ Last received latitude = %.2f Meters
+ The last received velocity was %.2f m/s (%.1f km/h)
+ This update was received at: %s
\ No newline at end of file