[ShareLocation] Added sharing of location logic.

Sharing of location is enabled yet, though no receiving
part is implemented as of now.
This commit is contained in:
Alexander Dörflinger
2025-03-19 12:38:08 +01:00
parent 47149307fe
commit 89804eb842
15 changed files with 646 additions and 31 deletions

View File

@@ -5,6 +5,9 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<application
android:allowBackup="true"
@@ -39,6 +42,13 @@
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
</intent-filter>
</receiver>
<receiver android:name=".utils.CancelLocationSharingReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.aldo.apps.familyhelpers.CANCEL_LOCATION_SHARING" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -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);
}
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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.

View File

@@ -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<Context> 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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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.
}

View File

@@ -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.
*/

View File

@@ -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();
}
}

View File

@@ -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<Context> 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<LocationObject> 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<LocationObject> 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<Location> 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);
}
}

View File

@@ -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.
*

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="800dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M21.616,25.777C21.433,25.927 21.216,26 21,26c-0.297,0 -0.591,-0.139 -0.789,-0.404L17,21.087v5.81C17,27.506 16.552,28 16,28s-1,-0.494 -1,-1.102v-5.81l-3.212,4.509c-0.34,0.458 -0.968,0.538 -1.404,0.181c-0.435,-0.358 -0.512,-1.019 -0.172,-1.477l5,-6.917c0.379,-0.511 1.197,-0.511 1.576,0l5,6.917C22.128,24.758 22.051,25.419 21.616,25.777zM6.803,20.404c-0.329,-0.443 -0.956,-0.536 -1.399,-0.206c-0.443,0.329 -0.536,0.956 -0.206,1.399c0.329,0.443 0.956,0.536 1.399,0.206C7.04,21.473 7.132,20.847 6.803,20.404z"
android:fillColor="#000000"/>
<path
android:pathData="M13.97,13.283l1.719,2.314c0.178,0.239 0.108,0.58 -0.149,0.73l-6.952,4.057c-0.22,0.128 -0.501,0.071 -0.653,-0.134l-0.659,-0.887C7.124,19.159 7.15,18.873 7.337,18.7l5.892,-5.484C13.446,13.012 13.793,13.044 13.97,13.283zM21.667,4.498l-6.933,7.435c-0.17,0.176 -0.187,0.449 -0.041,0.645l1.88,2.531c0.146,0.196 0.412,0.258 0.629,0.147l9.12,-4.492c0.275,-0.141 0.357,-0.495 0.173,-0.743l-4.067,-5.474C22.244,4.299 21.881,4.276 21.667,4.498z"
android:fillColor="#000000"/>
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="800dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9.611,12.4L10.818,18.535C11.046,19.694 12.603,19.924 13.156,18.882L19.021,7.843C19.248,7.416 19.201,6.944 18.974,6.584M9.611,12.4L5.226,8.155C4.417,7.371 4.972,6 6.099,6H17.913C18.376,6 18.757,6.241 18.974,6.584M9.611,12.4L18.974,6.584M19.056,6.533L18.974,6.584"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"/>
</vector>

View File

@@ -21,5 +21,7 @@
<!-- Share Location Funtionality 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>
</resources>