Repository: Baseflow/flutter-geolocator

Issue:
Custom Android notification icon color
## 🚀 Feature Requests

Right now, only the notification title, message, and icon can be customized. It would be great if we could also customize the icon's (background) color, instead of being forced to use the system's default.

### Contextualize the feature
The framework creates the Android `NotificationBuilder`, but doesn't expose many of the available methods to Flutter.

### Platforms affected (mark all that apply)
- [ ] :iphone: iOS
- [x] :robot: Android


Discussion: Any updates on this? Would need the same. 
Is there any update for notification icon?

Fix patch:
diff --git a/geolocator_android/CHANGELOG.md b/geolocator_android/CHANGELOG.md
index 461405f39..2252b527f 100644
--- a/geolocator_android/CHANGELOG.md
+++ b/geolocator_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 4.4.0
+
+- Adds `color` to `ForegroundNotificationConfig` to set the color of the notification icon.
+
 ## 4.3.3
 
 - Removes deprecated support for Android V1 embedding from the JAVA code base. Note that the geolocator's Flutter version restrictions already don't support V1 embedding anymore.
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/BackgroundNotification.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/BackgroundNotification.java
index 70285a3ee..0bd2ca038 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/BackgroundNotification.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/BackgroundNotification.java
@@ -10,6 +10,7 @@
 import android.os.Build;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.core.app.NotificationCompat;
 import androidx.core.app.NotificationManagerCompat;
 
@@ -24,10 +25,10 @@ public class BackgroundNotification {
     private NotificationCompat.Builder builder;
 
     public BackgroundNotification(
-            Context context,
-            String channelId ,
-            Integer notificationId,
-            ForegroundNotificationOptions options
+        @NonNull Context context,
+        @NonNull String channelId ,
+        @NonNull Integer notificationId,
+        ForegroundNotificationOptions options
     ) {
         this.context = context;
         this.notificationId = notificationId;
@@ -88,6 +89,12 @@ private void updateNotification(
                 .setContentIntent(buildBringToFrontIntent())
                 .setOngoing(options.isSetOngoing());
 
+        @Nullable final Integer notificationColor = options.getColor();
+        if (notificationColor != null) {
+            builder = builder
+                .setColor(notificationColor);
+        }
+
         if (notify) {
             NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
             notificationManager.notify(notificationId, builder.build());
diff --git a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/ForegroundNotificationOptions.java b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/ForegroundNotificationOptions.java
index 89d5f49d5..f4ce71811 100644
--- a/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/ForegroundNotificationOptions.java
+++ b/geolocator_android/android/src/main/java/com/baseflow/geolocator/location/ForegroundNotificationOptions.java
@@ -14,13 +14,11 @@ public class ForegroundNotificationOptions {
     private final String notificationText;
     @NonNull
     private final AndroidIconResource notificationIcon;
-    @NonNull
     private final boolean enableWifiLock;
-    @NonNull
     private final boolean enableWakeLock;
-
-    @NonNull
     private final boolean setOngoing;
+    @Nullable
+    private final Integer color;
 
 
     public static ForegroundNotificationOptions parseArguments(@Nullable  Map<String, Object> arguments) {
@@ -28,13 +26,18 @@ public static ForegroundNotificationOptions parseArguments(@Nullable  Map<String
       return null;
     }
 
-   final AndroidIconResource notificationIcon = AndroidIconResource.parseArguments((Map<String, Object>)arguments.get("notificationIcon"));
+    final AndroidIconResource notificationIcon = AndroidIconResource.parseArguments((Map<String, Object>)arguments.get("notificationIcon"));
     final String notificationTitle = (String) arguments.get("notificationTitle");
     final String notificationText = (String) arguments.get("notificationText");
     final Boolean enableWifiLock = (Boolean) arguments.get("enableWifiLock");
     final Boolean enableWakeLock = (Boolean) arguments.get("enableWakeLock");
     final Boolean setOngoing = (Boolean) arguments.get("setOngoing");
 
+    @Nullable Integer color = null;
+    final Object colorObject = arguments.get("color");
+    if (colorObject != null) {
+        color = ((Number) colorObject).intValue();
+    }
 
     return new ForegroundNotificationOptions(
             notificationTitle,
@@ -42,26 +45,38 @@ public static ForegroundNotificationOptions parseArguments(@Nullable  Map<String
             notificationIcon,
             enableWifiLock,
             enableWakeLock,
-            setOngoing);
+            setOngoing,
+            color);
   }
 
-    private ForegroundNotificationOptions(String notificationTitle, String notificationText, AndroidIconResource notificationIcon, boolean enableWifiLock, boolean enableWakeLock, boolean setOngoing) {
+    private ForegroundNotificationOptions(
+        @NonNull String notificationTitle,
+        @NonNull String notificationText,
+        @NonNull AndroidIconResource notificationIcon,
+        boolean enableWifiLock,
+        boolean enableWakeLock,
+        boolean setOngoing,
+        @Nullable Integer color) {
         this.notificationTitle = notificationTitle;
         this.notificationText = notificationText;
         this.notificationIcon = notificationIcon;
         this.enableWifiLock = enableWifiLock;
         this.enableWakeLock = enableWakeLock;
         this.setOngoing = setOngoing;
+        this.color = color;
     }
 
+    @NonNull
     public String getNotificationTitle() {
         return notificationTitle;
     }
 
+    @NonNull
     public String getNotificationText() {
         return notificationText;
     }
 
+    @NonNull
     public AndroidIconResource getNotificationIcon() {
         return notificationIcon;
     }
@@ -78,4 +93,7 @@ public boolean isSetOngoing() {
         return setOngoing;
     }
 
+    @Nullable public Integer getColor() {
+        return color;
+    }
 }
diff --git a/geolocator_android/example/lib/main.dart b/geolocator_android/example/lib/main.dart
index 3687cd5bf..ced77bbde 100644
--- a/geolocator_android/example/lib/main.dart
+++ b/geolocator_android/example/lib/main.dart
@@ -15,7 +15,7 @@ void main() {
   runApp(const GeolocatorWidget());
 }
 
-/// Example [Widget] showing the functionalities of the geolocator plugin
+/// Example [Widget] showing the functionalities of the geolocator plugin.
 class GeolocatorWidget extends StatefulWidget {
   /// Creates a new GeolocatorWidget.
   const GeolocatorWidget({Key? key}) : super(key: key);
@@ -297,13 +297,15 @@ class _GeolocatorWidgetState extends State<GeolocatorWidget> {
         forceLocationManager: false,
         useMSLAltitude: true,
         foregroundNotificationConfig: const ForegroundNotificationConfig(
+          // Explain to the user why we are showing this notification.
           notificationText:
               "Example app will continue to receive your location even when you aren't using it",
-          //Explain to the user why we are showing this notification
+          // Tell the user what we are doing.
           notificationTitle: "Running in Background",
-          //Tell the user what we are doing
-          enableWakeLock:
-              false, //Keep the system awake to receive background location information.
+          // Keep the system awake to receive background location information.
+          enableWakeLock: false,
+          // Give the notification an amber color.
+          color: Colors.amber,
         ),
       );
       final positionStream = geolocatorAndroid.getPositionStream(
diff --git a/geolocator_android/lib/src/types/foreground_settings.dart b/geolocator_android/lib/src/types/foreground_settings.dart
index 142fe848a..3ec3d3923 100644
--- a/geolocator_android/lib/src/types/foreground_settings.dart
+++ b/geolocator_android/lib/src/types/foreground_settings.dart
@@ -1,13 +1,20 @@
+import 'dart:ui';
+
 /// Uniquely identifies an Android resource.
 class AndroidResource {
   /// The name of the desired resource.
   final String name;
 
-  /// Optional default resource type to find, if "type/" is not included in the name. Can be null to require an explicit type.
+  /// Optional default resource type to find, if "type/" is not included in the name.
+  ///
+  /// Can be null to require an explicit type.
   final String defType;
 
   /// Uniquely identifies an Android resource.
-  const AndroidResource({required this.name, this.defType = 'drawable'});
+  const AndroidResource({
+    required this.name,
+    this.defType = 'drawable',
+  });
 
   /// Returns a JSON representation of this class.
   Map<String, dynamic> toJson() {
@@ -51,16 +58,36 @@ class ForegroundNotificationConfig {
   /// the user cannot dismiss it
   final bool setOngoing;
 
+  /// Accent color (an ARGB integer like the constants in Color) to be applied
+  /// by the standard Style templates when presenting this notification.
+  ///
+  /// The current template design constructs a colorful header image by
+  /// overlaying the icon image (stenciled in white) atop a field of this color.
+  /// Alpha components are ignored.
+  ///
+  /// If this color is null, a system default color is used.
+  final Color? color;
+
   /// Creates an Android specific configuration for the [FlutterBackground] plugin.
   ///
-  /// [notificationTitle] is the title used for the foreground service notification.
-  /// [notificationText] is the body used for the foreground service notification.
+  /// [notificationTitle] is the title used for the foreground service
+  /// notification.
+  /// [notificationText] is the body used for the foreground service
+  /// notification.
   /// [notificationIcon] must be a drawable resource.
-  /// E. g. if the icon with name "background_icon" is in the "drawable" resource folder,
-  /// it should be of value `AndroidResource(name: 'background_icon', defType: 'drawable').
+  /// E. g. if the icon with name "background_icon" is in the "drawable"
+  /// resource folder, it should be of value
+  /// `AndroidResource(name: 'background_icon', defType: 'drawable').
   /// [enableWifiLock] indicates wether or not a WifiLock is acquired, when the
   /// background execution is started. This allows the application to keep the
   /// Wi-Fi radio awake, even when the user has not used the device in a while.
+  /// [enableWakeLock] indicates wether or not a Wakelock is acquired, when the
+  /// background execution is started. If this is false then the system can
+  /// still sleep and all location events will be received at once when the
+  /// system wakes up again.
+  /// [setOngoing] indicates wether or not the displayed notification is
+  /// persistent and the user cannot dismiss it.
+  /// [color] is the accent color that is applied to the [notificationIcon].
   const ForegroundNotificationConfig({
     required this.notificationTitle,
     required this.notificationText,
@@ -69,17 +96,19 @@ class ForegroundNotificationConfig {
     this.enableWifiLock = false,
     this.enableWakeLock = false,
     this.setOngoing = false,
+    this.color,
   });
 
   /// Returns a JSON representation of this class.
   Map<String, dynamic> toJson() {
     return {
+      'enableWakeLock': enableWakeLock,
+      'enableWifiLock': enableWifiLock,
       'notificationTitle': notificationTitle,
-      'notificationText': notificationText,
       'notificationIcon': notificationIcon.toJson(),
-      'enableWifiLock': enableWifiLock,
-      'enableWakeLock': enableWakeLock,
+      'notificationText': notificationText,
       'setOngoing': setOngoing,
+      'color': color?.value,
     };
   }
 }
diff --git a/geolocator_android/pubspec.yaml b/geolocator_android/pubspec.yaml
index d4c55d3e8..5b98eb575 100644
--- a/geolocator_android/pubspec.yaml
+++ b/geolocator_android/pubspec.yaml
@@ -2,7 +2,7 @@ name: geolocator_android
 description: Geolocation plugin for Flutter. This plugin provides the Android implementation for the geolocator.
 repository: https://github.com/baseflow/flutter-geolocator/tree/main/geolocator_android
 issue_tracker: https://github.com/baseflow/flutter-geolocator/issues?q=is%3Aissue+is%3Aopen
-version: 4.3.3
+version: 4.4.0
 
 environment:
   sdk: ">=2.15.0 <4.0.0"


Test patch:
diff --git a/geolocator_android/test/geolocator_android_test.dart b/geolocator_android/test/geolocator_android_test.dart
index bd217fccd..447401d19 100644
--- a/geolocator_android/test/geolocator_android_test.dart
+++ b/geolocator_android/test/geolocator_android_test.dart
@@ -1,6 +1,7 @@
 import 'dart:async';
 
 import 'package:async/async.dart';
+import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:geolocator_android/geolocator_android.dart';
@@ -1433,19 +1434,25 @@ void main() {
           () async {
         // Arrange
         final settings = AndroidSettings(
-            accuracy: LocationAccuracy.best,
-            distanceFilter: 5,
-            forceLocationManager: false,
-            intervalDuration: const Duration(seconds: 1),
-            timeLimit: const Duration(seconds: 1),
-            useMSLAltitude: false,
-            foregroundNotificationConfig: const ForegroundNotificationConfig(
-                notificationText: 'text',
-                notificationTitle: 'title',
-                enableWakeLock: false,
-                enableWifiLock: false,
-                notificationIcon:
-                    AndroidResource(name: 'name', defType: 'defType')));
+          accuracy: LocationAccuracy.best,
+          distanceFilter: 5,
+          forceLocationManager: false,
+          intervalDuration: const Duration(seconds: 1),
+          timeLimit: const Duration(seconds: 1),
+          useMSLAltitude: false,
+          foregroundNotificationConfig: const ForegroundNotificationConfig(
+            color: Colors.amber,
+            enableWakeLock: false,
+            enableWifiLock: false,
+            notificationIcon: AndroidResource(
+              name: 'name',
+              defType: 'defType',
+            ),
+            notificationText: 'text',
+            notificationTitle: 'title',
+            setOngoing: true,
+          ),
+        );
 
         // Act
         final jsonMap = settings.toJson();
@@ -1471,6 +1478,39 @@ void main() {
           jsonMap['useMSLAltitude'],
           settings.useMSLAltitude,
         );
+        expect(
+          jsonMap['foregroundNotificationConfig']['enableWakeLock'],
+          settings.foregroundNotificationConfig!.enableWakeLock,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['enableWifiLock'],
+          settings.foregroundNotificationConfig!.enableWifiLock,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['notificationIcon']['name'],
+          settings.foregroundNotificationConfig!.notificationIcon.name,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['notificationIcon']
+              ['defType'],
+          settings.foregroundNotificationConfig!.notificationIcon.defType,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['notificationText'],
+          settings.foregroundNotificationConfig!.notificationText,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['notificationTitle'],
+          settings.foregroundNotificationConfig!.notificationTitle,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['setOngoing'],
+          settings.foregroundNotificationConfig!.setOngoing,
+        );
+        expect(
+          jsonMap['foregroundNotificationConfig']['color'],
+          settings.foregroundNotificationConfig!.color!.value,
+        );
       });
 
       test('Should receive false if an error occurred', () async {