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