Skip to content

Commit

Permalink
Add ability to update android notification; (#125)
Browse files Browse the repository at this point in the history
* Handle update notification call in iOS;

* Handle update notification call in Android;

* Add more option for updating notification;
  • Loading branch information
mehdok authored Aug 17, 2020
1 parent 09a93d4 commit 8651ec6
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rekab.app.background_locator

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent
import android.content.Context
Expand Down Expand Up @@ -44,6 +45,7 @@ import rekab.app.background_locator.Keys.Companion.METHOD_PLUGIN_IS_REGISTER_LOC
import rekab.app.background_locator.Keys.Companion.METHOD_PLUGIN_IS_SERVICE_RUNNING
import rekab.app.background_locator.Keys.Companion.METHOD_PLUGIN_REGISTER_LOCATION_UPDATE
import rekab.app.background_locator.Keys.Companion.METHOD_PLUGIN_UN_REGISTER_LOCATION_UPDATE
import rekab.app.background_locator.Keys.Companion.METHOD_PLUGIN_UPDATE_NOTIFICATION
import rekab.app.background_locator.Keys.Companion.NOTIFICATION_ACTION
import rekab.app.background_locator.Keys.Companion.NOTIFICATION_CALLBACK_HANDLE_KEY
import rekab.app.background_locator.Keys.Companion.SETTINGS_ACCURACY
Expand Down Expand Up @@ -72,6 +74,7 @@ class BackgroundLocatorPlugin
@JvmStatic
private var channel: MethodChannel? = null

@SuppressLint("MissingPermission")
@JvmStatic
private fun registerLocator(context: Context,
client: FusedLocationProviderClient,
Expand Down Expand Up @@ -217,6 +220,26 @@ class BackgroundLocatorPlugin
return
}

@JvmStatic
private fun updateNotificationText(context: Context, args: Map<Any, Any>) {
val intent = Intent(context, IsolateHolderService::class.java)
intent.action = IsolateHolderService.ACTION_UPDATE_NOTIFICATION
if (args.containsKey(SETTINGS_ANDROID_NOTIFICATION_TITLE)) {
intent.putExtra(SETTINGS_ANDROID_NOTIFICATION_TITLE,
args[SETTINGS_ANDROID_NOTIFICATION_TITLE] as String)
}
if (args.containsKey(SETTINGS_ANDROID_NOTIFICATION_MSG)) {
intent.putExtra(SETTINGS_ANDROID_NOTIFICATION_MSG,
args[SETTINGS_ANDROID_NOTIFICATION_MSG] as String)
}
if (args.containsKey(SETTINGS_ANDROID_NOTIFICATION_BIG_MSG)) {
intent.putExtra(SETTINGS_ANDROID_NOTIFICATION_BIG_MSG,
args[SETTINGS_ANDROID_NOTIFICATION_BIG_MSG] as String)
}

ContextCompat.startForegroundService(context, intent)
}

@JvmStatic
private fun setCallbackDispatcherHandle(context: Context, handle: Long) {
context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
Expand Down Expand Up @@ -314,6 +337,11 @@ class BackgroundLocatorPlugin
locatorClient)
METHOD_PLUGIN_IS_REGISTER_LOCATION_UPDATE -> isRegisterLocator(result)
METHOD_PLUGIN_IS_SERVICE_RUNNING -> isServiceRunning(result)
METHOD_PLUGIN_UPDATE_NOTIFICATION -> {
val args: Map<Any, Any> = call.arguments()
updateNotificationText(context!!, args)
result.success(true)
}
else -> result.notImplemented()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package rekab.app.background_locator

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.PowerManager
import android.util.Log
import androidx.core.app.NotificationCompat
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterNativeView
Expand Down Expand Up @@ -40,6 +38,9 @@ class IsolateHolderService : Service() {
@JvmStatic
val ACTION_START = "START"

@JvmStatic
val ACTION_UPDATE_NOTIFICATION = "UPDATE_NOTIFICATION"

@JvmStatic
private val WAKELOCK_TAG = "IsolateHolderService::WAKE_LOCK"

Expand Down Expand Up @@ -88,6 +89,7 @@ class IsolateHolderService : Service() {
private var notificationIconColor = 0
private var icon = 0
private var wakeLockTime = 60 * 60 * 1000L // 1 hour default wake lock time
private val notificationId = 1

override fun onBind(intent: Intent?): IBinder? {
return null
Expand All @@ -98,6 +100,24 @@ class IsolateHolderService : Service() {
return
}

(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG).apply {
setReferenceCounted(false)
acquire(wakeLockTime)
}
}

instance = this
sendInit()

// Starting Service as foreground with a notification prevent service from closing
val notification = getNotification()
startForeground(notificationId, notification)

isRunning = true
}

private fun getNotification(): Notification {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Notification channel is available in Android O and up
val channel = NotificationChannel(CHANNEL_ID, notificationChannelName,
Expand All @@ -110,9 +130,10 @@ class IsolateHolderService : Service() {
val intent = Intent(this, getMainActivityClass(this))
intent.action = NOTIFICATION_ACTION

val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val pendingIntent: PendingIntent = PendingIntent.getActivity(this,
1, intent, PendingIntent.FLAG_UPDATE_CURRENT)

val notification = NotificationCompat.Builder(this, CHANNEL_ID)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(notificationTitle)
.setContentText(notificationMsg)
.setStyle(NotificationCompat.BigTextStyle()
Expand All @@ -121,22 +142,9 @@ class IsolateHolderService : Service() {
.setColor(notificationIconColor)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setOnlyAlertOnce(true) // so when data is updated don't make sound and alert in android 8.0+
.setOngoing(true)
.build()

(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG).apply {
setReferenceCounted(false)
acquire(wakeLockTime)
}
}

instance = this
sendInit()

// Starting Service as foreground with a notification prevent service from closing
startForeground(1, notification)

isRunning = true
}

private fun stop() {
Expand All @@ -163,10 +171,16 @@ class IsolateHolderService : Service() {
return super.onStartCommand(intent, flags, startId)
}

if (intent.action == ACTION_SHUTDOWN) {
shutdownHolderService()
} else if (intent.action == ACTION_START) {
startHolderService(intent)
when {
ACTION_SHUTDOWN == intent.action -> {
shutdownHolderService()
}
ACTION_START == intent.action -> {
startHolderService(intent)
}
ACTION_UPDATE_NOTIFICATION == intent.action -> {
updateNotification(intent)
}
}

return START_STICKY
Expand Down Expand Up @@ -201,6 +215,24 @@ class IsolateHolderService : Service() {
stop()
}

private fun updateNotification(intent: Intent) {
if (intent.hasExtra(SETTINGS_ANDROID_NOTIFICATION_TITLE)) {
notificationTitle = intent.getStringExtra(SETTINGS_ANDROID_NOTIFICATION_TITLE)
}

if (intent.hasExtra(SETTINGS_ANDROID_NOTIFICATION_MSG)) {
notificationMsg = intent.getStringExtra(SETTINGS_ANDROID_NOTIFICATION_MSG)
}

if (intent.hasExtra(SETTINGS_ANDROID_NOTIFICATION_BIG_MSG)) {
notificationBigMsg = intent.getStringExtra(SETTINGS_ANDROID_NOTIFICATION_BIG_MSG)
}

val notification = getNotification()
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(notificationId, notification)
}

private fun getMainActivityClass(context: Context): Class<*>? {
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
Expand Down
2 changes: 2 additions & 0 deletions android/src/main/kotlin/rekab/app/background_locator/Keys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class Keys {
val METHOD_PLUGIN_IS_REGISTER_LOCATION_UPDATE = "LocatorPlugin.isRegisterLocationUpdate"
@JvmStatic
val METHOD_PLUGIN_IS_SERVICE_RUNNING = "LocatorPlugin.isServiceRunning"
@JvmStatic
val METHOD_PLUGIN_UPDATE_NOTIFICATION = "LocatorPlugin.updateNotification"

@JvmStatic
val ARG_IS_MOCKED = "is_mocked"
Expand Down
1 change: 1 addition & 0 deletions example/lib/location_service_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class LocationServiceRepository {
final SendPort send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(locationDto);
_count++;

}

static Future<void> setLogLabel(String label) async {
Expand Down
17 changes: 15 additions & 2 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class _MyAppState extends State<MyApp> {

Future<void> updateUI(LocationDto data) async {
final log = await FileManager.readLogFile();

await _updateNotificationText(data);

setState(() {
if (data != null) {
lastLocation = data;
Expand All @@ -67,6 +70,17 @@ class _MyAppState extends State<MyApp> {
});
}

Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}

await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}

Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
Expand Down Expand Up @@ -222,8 +236,7 @@ class _MyAppState extends State<MyApp> {
*/
disposeCallback: LocationCallbackHandler.disposeCallback,
iosSettings: IOSSettings(
accuracy: LocationAccuracy.NAVIGATION,
distanceFilter: 0),
accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 0),
autoStop: false,
androidSettings: AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
Expand Down
1 change: 1 addition & 0 deletions ios/Classes/Globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ FOUNDATION_EXPORT NSString *const kMethodPluginRegisterLocationUpdate;
FOUNDATION_EXPORT NSString *const kMethodPluginUnRegisterLocationUpdate;
FOUNDATION_EXPORT NSString *const kMethodPluginIsRegisteredLocationUpdate;
FOUNDATION_EXPORT NSString *const kMethodPluginIsServiceRunning;
FOUNDATION_EXPORT NSString *const kMethodPluginUpdateNotification;

FOUNDATION_EXPORT NSString *const kArgLatitude;
FOUNDATION_EXPORT NSString *const kArgLongitude;
Expand Down
1 change: 1 addition & 0 deletions ios/Classes/Globals.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ @implementation Globals
NSString *const kMethodPluginUnRegisterLocationUpdate = @"LocatorPlugin.unRegisterLocationUpdate";
NSString *const kMethodPluginIsRegisteredLocationUpdate = @"LocatorPlugin.isRegisterLocationUpdate";
NSString *const kMethodPluginIsServiceRunning = @"LocatorPlugin.isServiceRunning";
NSString *const kMethodPluginUpdateNotification = @"LocatorPlugin.updateNotification";

NSString *const kArgLatitude = @"latitude";
NSString *const kArgLongitude = @"longitude";
Expand Down
3 changes: 3 additions & 0 deletions ios/Classes/Helpers/MethodCallHelper.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call
}else if ([kMethodPluginIsServiceRunning isEqualToString:call.method]) {
BOOL val = [delegate isServiceRunning];
result(@(val));
} else if([kMethodPluginUpdateNotification isEqualToString:call.method]) {
// updating notification's text is just for android
result(nil);
} else {
result(FlutterMethodNotImplemented);
}
Expand Down
19 changes: 19 additions & 0 deletions lib/background_locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,23 @@ class BackgroundLocator {
return await _channel
.invokeMethod<bool>(Keys.METHOD_PLUGIN_IS_SERVICE_RUNNING);
}

static Future<void> updateNotificationText(
{String title, String msg, String bigMsg}) async {
final Map<String, dynamic> arg = {};

if (title != null) {
arg[Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE] = title;
}

if (msg != null) {
arg[Keys.SETTINGS_ANDROID_NOTIFICATION_MSG] = msg;
}

if (bigMsg != null) {
arg[Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG] = bigMsg;
}

await _channel.invokeMethod(Keys.METHOD_PLUGIN_UPDATE_NOTIFICATION, arg);
}
}
2 changes: 2 additions & 0 deletions lib/keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class Keys {
'LocatorPlugin.isRegisterLocationUpdate';
static const String METHOD_PLUGIN_IS_SERVICE_RUNNING =
'LocatorPlugin.isServiceRunning';
static const String METHOD_PLUGIN_UPDATE_NOTIFICATION =
'LocatorPlugin.updateNotification';

static const String ARG_IS_MOCKED = 'is_mocked';
static const String ARG_LATITUDE = 'latitude';
Expand Down

0 comments on commit 8651ec6

Please sign in to comment.