[ShareLocation] Initial "read data from db" logic
Added some base GoogleMap widget logic and a way to listen to ones own location updates. In future this needs to be extended with a way to listen to the updates of other users.
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
tools:targetApi="31">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
android:value="AIzaSyBrwogmBLNjbhngmON_RWhKrV1woF6ypR4" />
|
||||
<activity
|
||||
android:name=".HelperGridActivity"
|
||||
android:exported="true">
|
||||
@@ -29,11 +33,16 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name=".workers.SleepTimerHelper" android:foregroundServiceType="specialUse"/>
|
||||
<activity android:name=".ShareLocationActivity"/>
|
||||
|
||||
<receiver android:name="com.aldo.apps.familyhelpers.DoerflingerHelpersDeviceAdminReceiver"
|
||||
android:permission="android.permission.BIND_DEVICE_ADMIN"
|
||||
android:exported="true">
|
||||
<service
|
||||
android:name=".workers.SleepTimerHelper"
|
||||
android:foregroundServiceType="specialUse" />
|
||||
|
||||
<receiver
|
||||
android:name="com.aldo.apps.familyhelpers.DoerflingerHelpersDeviceAdminReceiver"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_DEVICE_ADMIN">
|
||||
<meta-data
|
||||
android:name="android.app.device_admin"
|
||||
android:resource="@xml/device_admin" />
|
||||
@@ -43,7 +52,8 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".utils.CancelLocationSharingReceiver"
|
||||
<receiver
|
||||
android:name=".utils.CancelLocationSharingReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.aldo.apps.familyhelpers.CANCEL_LOCATION_SHARING" />
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<Context> mContextRef;
|
||||
private final WeakReference<Context> 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 + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<List<User>> mAllUsers = BehaviorSubject.create();
|
||||
|
||||
/**
|
||||
* Local {@link BehaviorSubject} representation holding the {@link LocationObject} that is to be observed.
|
||||
*/
|
||||
private BehaviorSubject<LocationObject> 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<List<User>> 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<LocationObject> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<LocationObject> mLocationSubject = BehaviorSubject.create();
|
||||
private final BehaviorSubject<LocationObject> 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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
13
app/src/main/res/drawable/ic_current_location.xml
Normal file
13
app/src/main/res/drawable/ic_current_location.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M8,10C9.105,10 10,9.105 10,8C10,6.895 9.105,6 8,6C6.895,6 6,6.895 6,8C6,9.105 6.895,10 8,10Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M2.083,7C2.504,4.487 4.487,2.504 7,2.083V0H9V2.083C11.512,2.504 13.495,4.487 13.917,7H16V9H13.917C13.495,11.512 11.512,13.495 9,13.917V16H7V13.917C4.487,13.495 2.504,11.512 2.083,9H0V7H2.083ZM4,8C4,5.791 5.791,4 8,4C10.209,4 12,5.791 12,8C12,10.209 10.209,12 8,12C5.791,12 4,10.209 4,8Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
69
app/src/main/res/layout/activity_share_location.xml
Normal file
69
app/src/main/res/layout/activity_share_location.xml
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ShareLocationActivity">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/map"
|
||||
android:name="com.google.android.gms.maps.SupportMapFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/share_location_info_box"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/share_location_info_box"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/share_location_info_user_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_location_info_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:textStyle="bold"
|
||||
tools:text="@string/share_location_info_title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_location_info_location"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_location_info_title"
|
||||
tools:text="@string/share_location_info_location"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_location_info_altitude"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_location_info_location"
|
||||
tools:text="@string/share_location_info_altitude"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_location_info_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_location_info_altitude"
|
||||
tools:text="@string/share_location_info_speed"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_location_info_timestamp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/share_location_info_speed"
|
||||
tools:text="@string/share_location_info_timestamp"/>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
46
app/src/main/res/raw/map_style_day.json
Normal file
46
app/src/main/res/raw/map_style_day.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
205
app/src/main/res/raw/map_style_night.json
Normal file
205
app/src/main/res/raw/map_style_night.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -19,9 +19,14 @@
|
||||
<string name="sleep_timer_remaining_time_with_hour">%02d:%02d:%02d"</string>
|
||||
<string name="sleep_timer_remaining_time_without_hour">%02d:%02d"</string>
|
||||
|
||||
<!-- Share Location Funtionality Strings -->
|
||||
<!-- Share Location Functionality Strings -->
|
||||
<string name="title_share_location">Share Location</string>
|
||||
<string name="share_location_notification_channel">ShareLocation</string>
|
||||
<string name="share_location_notification_content">You are sharing your current location within the family</string>
|
||||
<string name="share_location_info_title">You are following %s</string>
|
||||
<string name="share_location_info_location">Last Received Location = %f;%f</string>
|
||||
<string name="share_location_info_altitude">Last received latitude = %.2f Meters</string>
|
||||
<string name="share_location_info_speed">The last received velocity was %.2f m/s (%.1f km/h)</string>
|
||||
<string name="share_location_info_timestamp">This update was received at: %s</string>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user