diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 66773c4..d2d6e8a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,9 @@
+
+
+
+
+
+
+
+
+
\ 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 8f68809..4b00439 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java
@@ -11,19 +11,19 @@ import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
-import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.aldo.apps.familyhelpers.ui.HelperGroupTile;
import com.aldo.apps.familyhelpers.ui.SleepTimerPopup;
import com.aldo.apps.familyhelpers.utils.DevicePolicyManagerHelper;
+import com.aldo.apps.familyhelpers.workers.LocationHelper;
import com.aldo.apps.familyhelpers.workers.DatabaseHelper;
import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
-import com.firebase.ui.auth.FirebaseUiException;
import com.firebase.ui.auth.IdpResponse;
import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
import com.google.firebase.auth.FirebaseAuth;
@@ -39,6 +39,8 @@ public class HelperGridActivity extends AppCompatActivity {
*/
private static final String TAG = "HelperGridActivity";
+ private LocationHelper mLocationHelper;
+
/**
* {@link HelperGroupTile} holding the option for a sleep timer.
*/
@@ -95,6 +97,7 @@ public class HelperGridActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ mLocationHelper = LocationHelper.getInstance(this);
mWelcomeMessageView = findViewById(R.id.tv_welcome_message);
if (mCurrentUser == null) {
final Intent signInIntent = AuthUI.getInstance()
@@ -156,9 +159,13 @@ public class HelperGridActivity extends AppCompatActivity {
@Override
public void launchHelper() {
Log.d(TAG, "launchHelper: Clicked ShareLocation");
+ if (mLocationHelper.requestLocationPermissions(HelperGridActivity.this)) {
+ Log.d(TAG, "launchHelper: Permission already granted");
+ mLocationHelper.toggleUpdate(HelperGridActivity.this);
+ }
}
};
- mShareLocationTile.setLogo(R.drawable.ic_share_location);
+ mShareLocationTile.setLogo(R.drawable.ic_location_helper);
mShareLocationTile.setTitle(R.string.title_share_location);
}
@@ -210,5 +217,14 @@ public class HelperGridActivity extends AppCompatActivity {
}
}
+ @Override
+ public void onRequestPermissionsResult(final int requestCode,
+ @NonNull final String[] permissions,
+ @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (mLocationHelper.handlePermissionResult(requestCode, grantResults)) {
+ mLocationHelper.toggleUpdate(HelperGridActivity.this);
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java b/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java
new file mode 100644
index 0000000..7fc5597
--- /dev/null
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java
@@ -0,0 +1,146 @@
+package com.aldo.apps.familyhelpers.model;
+
+import android.location.Location;
+
+import androidx.annotation.NonNull;
+
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+
+/**
+ * Model class for a {@link LocationObject}.
+ */
+public class LocationObject {
+ /**
+ * Unique identifier for the shared location.
+ */
+ private String shareId;
+
+ /**
+ * Last received latitude.
+ */
+ private double latitude;
+
+ /**
+ * Last received longitude.
+ */
+ private double longitude;
+
+ /**
+ * Last received altitude.
+ */
+ private double altitude;
+
+ /**
+ * Last received speed.
+ */
+ private float speed;
+
+ /**
+ * Last received timestamp.
+ */
+ private long timestamp;
+
+ /**
+ * Empty C'Tor for database usage.
+ */
+ public LocationObject() {
+ //Empty C'Tor for database usage.
+ }
+
+ /**
+ * C'Tor filling all arguments.
+ *
+ * @param shareId Unique identifier for the shared location.
+ * @param latitude Last received latitude.
+ * @param longitude Last received longitude.
+ * @param altitude Last received altitude.
+ * @param speed Last received speed.
+ * @param timestamp Last received timestamp.
+ */
+ public LocationObject(final String shareId, final double latitude, final double longitude,
+ final double altitude, final float speed, final long timestamp) {
+ this.shareId = shareId;
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.altitude = altitude;
+ this.speed = speed;
+ this.timestamp = timestamp;
+ }
+
+ /**
+ * Returns the unique identifier for the shared location.
+ *
+ * @return The unique identifier for the shared location.
+ */
+ public String getShareId() {
+ return shareId;
+ }
+
+ /**
+ * Returns the last received latitude.
+ *
+ * @return The last received latitude.
+ */
+ public double getLatitude() {
+ return latitude;
+ }
+
+ /**
+ * Returns the last received longitude.
+ *
+ * @return The last received longitude.
+ */
+ public double getLongitude() {
+ return longitude;
+ }
+
+ /**
+ * Returns the last received altitude.
+ *
+ * @return The last received altitude.
+ */
+ public double getAltitude() {
+ return altitude;
+ }
+
+ /**
+ * Returns the last received speed.
+ *
+ * @return The last received speed.
+ */
+ public float getSpeed() {
+ return speed;
+ }
+
+ /**
+ * Returns the last received timestamp.
+ *
+ * @return The last received timestamp.
+ */
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Helper method to create a {@link LocationObject} from a handed in {@link Location}.
+ *
+ * @param location The received {@link Location} from the locationManager.
+ *
+ * @return The created {@link LocationObject}.
+ */
+ public static LocationObject fromLocation(final Location location) {
+ final FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
+ return new LocationObject(firebaseUser.getUid(), location.getLatitude(),
+ location.getLongitude(), location.getAltitude(), location.getSpeed(),
+ location.getTime());
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "LocationObject with shareId = [" + shareId + "], lat = [" + latitude
+ + "], long = [" + longitude + "], alt = [" + altitude + "], speed = [" + speed
+ + "], time = [" + timestamp + "]";
+ }
+}
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/model/User.java b/app/src/main/java/com/aldo/apps/familyhelpers/model/User.java
index e6bd6ef..3ab4090 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/model/User.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/model/User.java
@@ -34,7 +34,7 @@ public class User {
private long creationDate;
/**
- * //Empty C'Tor for database usage.
+ * Empty C'Tor for database usage.
*/
public User() {
//Empty C'Tor for database usage.
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/ui/NotificationHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/ui/NotificationHelper.java
new file mode 100644
index 0000000..782d9a1
--- /dev/null
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/ui/NotificationHelper.java
@@ -0,0 +1,74 @@
+package com.aldo.apps.familyhelpers.ui;
+
+import static android.content.Context.NOTIFICATION_SERVICE;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+
+import androidx.annotation.StringRes;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Helper class to encapsulate common {@link Notification} related tasks.
+ */
+public class NotificationHelper {
+
+ /**
+ * {@link WeakReference} on the {@link Context}.
+ */
+ private final WeakReference mContextRef;
+
+ /**
+ * {@link NotificationManager} to show and update the notifications.
+ */
+ private final NotificationManager mNotificationManager;
+
+ /**
+ * C'Tor.
+ *
+ * @param context The {@link Context} from which this was called.
+ */
+ public NotificationHelper(final Context context) {
+ mContextRef = new WeakReference<>(context);
+ mNotificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ }
+
+ /**
+ * Creates or updates an existing {@link Notification}.
+ *
+ * @param notificationId The identifier of the {@link Notification}.
+ * @param notification The actual {@link Notification}.
+ */
+ public void updateNotification(final int notificationId, final Notification notification) {
+ mNotificationManager.notify(notificationId, notification);
+ }
+
+ /**
+ * Cancel an ongoing {@link Notification}.
+ *
+ * @param notificationId The ID of the notification to cancel.
+ */
+ public void cancelNotification(final int notificationId) {
+ mNotificationManager.cancel(notificationId);
+ }
+
+ /**
+ * Helper method to create and register a new {@link NotificationChannel}.
+ *
+ * @param channelId The ID of the channel to be created.
+ * @param channelLabel The label of the channel to be created.
+ */
+ public void createAndRegisterNotificationChannel(final String channelId,
+ @StringRes final int channelLabel) {
+ final Context context = mContextRef.get();
+ if (context != null) {
+ final NotificationChannel channel = new NotificationChannel(channelId,
+ context.getString(channelLabel),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ mNotificationManager.createNotificationChannel(channel);
+ }
+ }
+}
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/utils/CancelLocationSharingReceiver.java b/app/src/main/java/com/aldo/apps/familyhelpers/utils/CancelLocationSharingReceiver.java
new file mode 100644
index 0000000..671f2df
--- /dev/null
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/utils/CancelLocationSharingReceiver.java
@@ -0,0 +1,38 @@
+package com.aldo.apps.familyhelpers.utils;
+
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SHARE_LOCATION_CANCEL_ACTION;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.aldo.apps.familyhelpers.workers.LocationHelper;
+
+/**
+ * Broadcast received to cancel an ongoing sharing of location.
+ */
+public class CancelLocationSharingReceiver extends BroadcastReceiver {
+
+ /**
+ * Tag for debugging purpose.
+ */
+ private static final String TAG = "CancelLocationSharingRe";
+
+ /**
+ * Empty C'tor.
+ */
+ public CancelLocationSharingReceiver() {
+ // Empty C'tor.
+ }
+
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (intent != null && SHARE_LOCATION_CANCEL_ACTION.equalsIgnoreCase(intent.getAction())) {
+ Log.d(TAG, "onReceive: Notification cancelled, cancel location sharing");
+ final LocationHelper locationHelper = LocationHelper.getInstance(context);
+ locationHelper.stopLocationUpdates();
+ }
+ }
+}
diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/utils/DatabaseConstants.java b/app/src/main/java/com/aldo/apps/familyhelpers/utils/DatabaseConstants.java
index 262bb95..75ed4a5 100644
--- a/app/src/main/java/com/aldo/apps/familyhelpers/utils/DatabaseConstants.java
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/utils/DatabaseConstants.java
@@ -1,11 +1,33 @@
package com.aldo.apps.familyhelpers.utils;
+/**
+ * Utility class for database constants.
+ */
public final class DatabaseConstants {
+ /**
+ * Key for the collection of Users in the Database.
+ */
public static final String DB_COLL_USERS = "users";
+ /**
+ * The unique identifier of a user document.
+ */
public static final String DB_DOC_USER_ID = "uId";
+ /**
+ * Key for the collection of shared locations in the Database.
+ */
+ public static final String DB_COLL_LOCATION = "locations";
+
+ /**
+ * The unique identifier for the location sharing.
+ */
+ public static final String DB_DOC_SHARE_ID = "shareId";
+
+ /**
+ * Private C'Tor to prevent instantiation.
+ */
private DatabaseConstants() {
//Private C'Tor to prevent instantiation.
}
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 9c6cbd0..6d4c210 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
@@ -20,6 +20,11 @@ public final class GlobalConstants {
*/
public static final String SLEEP_TIMER_CHANNEL_ID = "CountdownChannel";
+ /**
+ * ID of the NotificationChannel for the ShareLocation Notification.
+ */
+ public static final String SHARE_LOCATION_CHANNEL_ID = "LocationShareChannel";
+
/**
* Factor to calculate seconds from milliseconds and vice versa.
*/
@@ -35,6 +40,16 @@ public final class GlobalConstants {
*/
public static final int SLEEP_TIMER_NOTIFICATION_ID = 1;
+ /**
+ * The NotificationID of the ShareLocation notification.
+ */
+ public static final int SHARE_LOCATION_NOTIFICATION_ID = 2;
+
+ /**
+ * The action to be invoked when the sharing of location should be cancelled.
+ */
+ public static final String SHARE_LOCATION_CANCEL_ACTION = "com.aldo.apps.familyhelpers.CANCEL_LOCATION_SHARING";
+
/**
* The key of the extra to be applied to the starting intent of the sleepTimer service,
* holding the initial duration in millis.
@@ -46,6 +61,18 @@ public final class GlobalConstants {
*/
public static final String SLEEP_TIMER_CANCEL_ACTION = "SLEEP_TIMER_CANCEL";
+ /**
+ * Default minimum time interval between two location updates.
+ * Currently set to 5 Seconds.
+ */
+ public static final int DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS = 5000;
+
+ /**
+ * Default minimum distance interval between two location updates.
+ * Currently set to 5 Meters.
+ */
+ public static final int DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS = 5;
+
/**
* 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 5a1b399..f94aea2 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
@@ -1,11 +1,13 @@
package com.aldo.apps.familyhelpers.workers;
+import static com.aldo.apps.familyhelpers.utils.DatabaseConstants.DB_COLL_LOCATION;
import static com.aldo.apps.familyhelpers.utils.DatabaseConstants.DB_COLL_USERS;
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;
@@ -80,7 +82,28 @@ public class DatabaseHelper {
Log.w(TAG, "onEvent: Read failed, do not update local values.");
}
});
+ }
+ /**
+ * Helper method to insert or update a {@link LocationObject} in the database.
+ *
+ * @param locationObject The {@link LocationObject} to be inserted/updated.
+ */
+ public void insertOrUpdateLocation(final LocationObject locationObject) {
+ mDatabase.collection(DB_COLL_LOCATION)
+ .document(locationObject.getShareId())
+ .set(locationObject);
+ }
+
+ /**
+ * Cancels (by deleting) the ongoing {@link LocationObject} sharing in the database.
+ */
+ public void cancelOngoingLocationSharing() {
+ if (mCurrentUser == null) {
+ Log.w(TAG, "cancelOngoingLocationSharing: No user logged in, cannot stop sharing");
+ return;
+ }
+ 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
new file mode 100644
index 0000000..a862fe9
--- /dev/null
+++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java
@@ -0,0 +1,253 @@
+package com.aldo.apps.familyhelpers.workers;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS;
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS;
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SHARE_LOCATION_CANCEL_ACTION;
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SHARE_LOCATION_CHANNEL_ID;
+import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SHARE_LOCATION_NOTIFICATION_ID;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.NotificationCompat;
+import androidx.core.content.ContextCompat;
+
+import com.aldo.apps.familyhelpers.R;
+import com.aldo.apps.familyhelpers.model.LocationObject;
+import com.aldo.apps.familyhelpers.ui.NotificationHelper;
+import com.aldo.apps.familyhelpers.utils.CancelLocationSharingReceiver;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+import io.reactivex.rxjava3.subjects.BehaviorSubject;
+
+/**
+ * Helper class to encapsulate all Location specific calls into one utility class.
+ */
+public final class LocationHelper implements LocationListener {
+
+ /**
+ * Tag for debugging purpose.
+ */
+ private static final String TAG = "LocationHelper";
+
+ /**
+ * The request code of the for the location permission request.
+ */
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+ /**
+ * The singleton instance of this {@link LocationHelper}.
+ */
+ private static LocationHelper sInstance;
+ private final WeakReference mContextRef;
+ /**
+ * The {@link LocationManager} object to listen to location updates.
+ */
+ private final LocationManager mLocationManager;
+
+ /**
+ * Boolean flag to check whether a subscription for location updates is already running or not.
+ */
+ private boolean mIsSubscribed;
+
+ /**
+ * The {@link BehaviorSubject} for the {@link LocationObject} for clients to subscribe to.
+ * TODO: Check if needed.
+ */
+ private BehaviorSubject mLocationSubject = BehaviorSubject.create();
+
+ /**
+ * The {@link DatabaseHelper} for db access.
+ */
+ private DatabaseHelper mDbHelper = new DatabaseHelper();
+
+ /**
+ * The {@link NotificationHelper} to show and update {@link Notification}s.
+ */
+ private NotificationHelper mNotificationHelper;
+
+ /**
+ * Private C'Tor for singleton instance.
+ *
+ * @param context The {@link Context) from where this was instantiated.
+ */
+ private LocationHelper(final Context context) {
+ mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ mContextRef = new WeakReference<>(context);
+ mNotificationHelper = new NotificationHelper(context);
+ mNotificationHelper.createAndRegisterNotificationChannel(
+ SHARE_LOCATION_CHANNEL_ID, R.string.share_location_notification_channel);
+ }
+
+ /**
+ * Returns the singleton instance of thie {@link LocationHelper}.
+ *
+ * @param context The {@link Context) from where this was instantiated.
+ * @return The singleton instance of thie {@link LocationHelper}.
+ */
+ public static LocationHelper getInstance(final Context context) {
+ if (sInstance == null) {
+ sInstance = new LocationHelper(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Returns the {@link BehaviorSubject} of {@link LocationObject} for clients to subscribe to.
+ *
+ * @return The {@link BehaviorSubject} of {@link LocationObject} for clients to subscribe to.
+ */
+ public BehaviorSubject getLocationSubject() {
+ return mLocationSubject;
+ }
+
+ /**
+ * Request the permission for {@link android.Manifest.permission#ACCESS_FINE_LOCATION} if not
+ * yet granted before.
+ *
+ * @param activity The {@link Activity} from where this was called.
+ * @return true if permission was already granted before, false otherwise.
+ */
+ public boolean requestLocationPermissions(final Activity activity) {
+ if (ContextCompat.checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(activity, new String[]{ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Helper method to handle the result of the permission request dialog.
+ *
+ * @param requestCode The code of the permission request.
+ * @param grantResults The result codes.
+ * @return true if permission was granted, false otherwise.
+ */
+ public boolean handlePermissionResult(final int requestCode, final int[] grantResults) {
+ if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
+ // Permission granted, proceed with location updates
+ return grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ }
+ return false;
+ }
+
+ /**
+ * Helper method to start listening for updates.
+ *
+ * @param activity The {@link Activity} from where this was called.
+ * @param minIntervalMillis The minimum time interval in millisecond
+ * @param minDistance The minimum distance for an update to be received.
+ * @return true if subscription started, false otherwise.
+ */
+ public boolean startLocationUpdates(final Activity activity, final int minIntervalMillis, final int minDistance) {
+ if (ContextCompat.checkSelfPermission(activity, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ mLocationManager.requestLocationUpdates(
+ LocationManager.FUSED_PROVIDER,
+ minIntervalMillis,
+ minDistance,
+ this,
+ Looper.getMainLooper()
+ );
+ mIsSubscribed = true;
+ return true;
+ }
+ mIsSubscribed = false;
+ return false;
+ }
+
+ /**
+ * Stops listening for updates.
+ */
+ public void stopLocationUpdates() {
+ mLocationManager.removeUpdates(this);
+ mIsSubscribed = false;
+ mDbHelper.cancelOngoingLocationSharing();
+ mNotificationHelper.cancelNotification(SHARE_LOCATION_NOTIFICATION_ID);
+ }
+
+ /**
+ * Helper method to toggle the subscription state, if already running, cancel it, if not yet,
+ * start it.
+ *
+ * @param activity The {@link Activity} from where this was called.
+ */
+ public void toggleUpdate(final Activity activity) {
+ if (mIsSubscribed) {
+ Log.d(TAG, "toggleUpdate: Stop subscription");
+ stopLocationUpdates();
+ } else {
+ Log.d(TAG, "toggleUpdate: Start subscription");
+ startLocationUpdates(activity, DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS,
+ DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS);
+ }
+ }
+
+ /**
+ * Build a notification to be shown while location sharing is in progress.
+ *
+ * @param context The {@link Context} from where this is called.
+ * @return The {@link Notification} to be shown.
+ */
+ private Notification buildNotification(final Context context) {
+ final Intent deleteIntent = new Intent(context, CancelLocationSharingReceiver.class);
+ deleteIntent.setAction(SHARE_LOCATION_CANCEL_ACTION);
+ final PendingIntent pendingDeleteIntent = PendingIntent.getBroadcast(context, 0,
+ deleteIntent, PendingIntent.FLAG_IMMUTABLE);
+
+ return new NotificationCompat.Builder(context, SHARE_LOCATION_CHANNEL_ID)
+ .setContentTitle(context.getString(R.string.title_share_location))
+ .setContentText(context.getString(R.string.share_location_notification_content))
+ .setSmallIcon(R.drawable.ic_location_helper)
+ .setDeleteIntent(pendingDeleteIntent)
+ .addAction(android.R.drawable.ic_menu_close_clear_cancel,
+ context.getString(R.string.sleep_timer_notification_cancel), pendingDeleteIntent)
+ .setStyle(new NotificationCompat.BigTextStyle())
+ .build();
+ }
+
+ @Override
+ public void onLocationChanged(@NonNull final Location location) {
+ final LocationObject locationObject = LocationObject.fromLocation(location);
+ mLocationSubject.onNext(locationObject);
+ mDbHelper.insertOrUpdateLocation(locationObject);
+ final Context context = mContextRef.get();
+ if (context != null) {
+ mNotificationHelper.updateNotification(SHARE_LOCATION_NOTIFICATION_ID, buildNotification(context));
+ }
+
+ Log.d(TAG, "onLocationChanged: Received update with " + locationObject);
+ }
+
+ @Override
+ public void onLocationChanged(@NonNull final List locations) {
+ LocationListener.super.onLocationChanged(locations);
+ }
+
+ @Override
+ public void onFlushComplete(final int requestCode) {
+ LocationListener.super.onFlushComplete(requestCode);
+ }
+
+ @Override
+ public void onProviderEnabled(@NonNull final String provider) {
+ LocationListener.super.onProviderEnabled(provider);
+ }
+
+ @Override
+ public void onProviderDisabled(@NonNull final String provider) {
+ LocationListener.super.onProviderDisabled(provider);
+ }
+}
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 16b3934..b6ec9d4 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
@@ -24,6 +24,7 @@ import androidx.core.app.NotificationCompat;
import com.aldo.apps.familyhelpers.HelperGridActivity;
import com.aldo.apps.familyhelpers.R;
+import com.aldo.apps.familyhelpers.ui.NotificationHelper;
import com.aldo.apps.familyhelpers.utils.DevicePolicyManagerHelper;
import java.util.concurrent.TimeUnit;
@@ -57,16 +58,16 @@ public class SleepTimerHelper extends Service {
private DevicePolicyManagerHelper mDevicePolicyHelper;
/**
- * {@link NotificationManager} to show and update the foreground service notification.
+ * The {@link NotificationHelper} to show and update {@link Notification}s.
*/
- private NotificationManager mNotificationManager;
+ private NotificationHelper mNotificationHelper;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() called");
mDevicePolicyHelper = DevicePolicyManagerHelper.getInstance(this);
- mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ mNotificationHelper = new NotificationHelper(this);
}
@Override
@@ -80,9 +81,9 @@ public class SleepTimerHelper extends Service {
return START_NOT_STICKY;
}
mCountdownTimeMillis = intent.getLongExtra(SLEEP_TIMER_DURATION_MILLIS_EXTRA, 0);
- createNotificationChannel();
+ mNotificationHelper.createAndRegisterNotificationChannel(SLEEP_TIMER_CHANNEL_ID, R.string.sleep_timer_notification_channel);
startForeground(SLEEP_TIMER_NOTIFICATION_ID, buildNotification());
- mNotificationManager.notify(SLEEP_TIMER_NOTIFICATION_ID, buildNotification());
+ mNotificationHelper.updateNotification(SLEEP_TIMER_NOTIFICATION_ID, buildNotification());
startCountdown();
return super.onStartCommand(intent, flags, startId);
}
@@ -102,7 +103,7 @@ public class SleepTimerHelper extends Service {
@Override
public void run() {
if (mCountdownTimeMillis > 0) {
- updateNotification();
+ mNotificationHelper.updateNotification(SLEEP_TIMER_NOTIFICATION_ID, buildNotification());
mCountdownTimeMillis -= ONE_SECOND_IN_MILLIS;
mHandler.postDelayed(this, ONE_SECOND_IN_MILLIS);
} else {
@@ -115,14 +116,6 @@ public class SleepTimerHelper extends Service {
mHandler.post(mCountdownRunnable);
}
- /**
- * Helper method to update the notification with the new time.
- */
- private void updateNotification() {
- final Notification notification = buildNotification();
- mNotificationManager.notify(SLEEP_TIMER_NOTIFICATION_ID, notification);
- }
-
/**
* Helper method to build the Notification to be shown showing the current progress of the countdown.#
*
@@ -143,25 +136,13 @@ public class SleepTimerHelper extends Service {
.setContentTitle(getString(R.string.title_sleep_timer))
.setContentText(String.format(getString(R.string.sleep_timer_notification_content),
countdownText))
- .setSmallIcon(android.R.drawable.ic_lock_idle_alarm)
+ .setSmallIcon(R.drawable.ic_sleep_timer)
.setContentIntent(pendingIntent)
.addAction(android.R.drawable.ic_menu_close_clear_cancel,
getString(R.string.sleep_timer_notification_cancel), cancelPendingIntent)
.build();
}
- /**
- * Creates ad registers a new {@link NotificationChannel}.
- */
- private void createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- final NotificationChannel channel = new NotificationChannel(SLEEP_TIMER_CHANNEL_ID,
- getString(R.string.sleep_timer_notification_channel),
- NotificationManager.IMPORTANCE_DEFAULT);
- mNotificationManager.createNotificationChannel(channel);
- }
- }
-
/**
* Formats the provided time in milliseconds in a human readable format.
*
diff --git a/app/src/main/res/drawable/ic_follow_location.xml b/app/src/main/res/drawable/ic_follow_location.xml
new file mode 100644
index 0000000..9592c81
--- /dev/null
+++ b/app/src/main/res/drawable/ic_follow_location.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_share_location.xml b/app/src/main/res/drawable/ic_location_helper.xml
similarity index 100%
rename from app/src/main/res/drawable/ic_share_location.xml
rename to app/src/main/res/drawable/ic_location_helper.xml
diff --git a/app/src/main/res/drawable/ic_location_share.xml b/app/src/main/res/drawable/ic_location_share.xml
new file mode 100644
index 0000000..93f017b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location_share.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 921b201..5d901ee 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -21,5 +21,7 @@
Share Location
+ ShareLocation
+ You are sharing your current location within the family
\ No newline at end of file