2014-10-26 16:47:18 +01:00
|
|
|
/*
|
2017-01-01 22:40:41 +01:00
|
|
|
Copyright 2013-2017 appPlant GmbH
|
2019-02-03 15:58:40 +01:00
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
or more contributor license agreements. See the NOTICE file
|
|
|
|
distributed with this work for additional information
|
|
|
|
regarding copyright ownership. The ASF licenses this file
|
|
|
|
to you under the Apache License, Version 2.0 (the
|
|
|
|
"License"); you may not use this file except in compliance
|
|
|
|
with the License. You may obtain a copy of the License at
|
2019-02-03 15:58:40 +01:00
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
2019-02-03 15:58:40 +01:00
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
Unless required by applicable law or agreed to in writing,
|
|
|
|
software distributed under the License is distributed on an
|
|
|
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
|
|
KIND, either express or implied. See the License for the
|
|
|
|
specific language governing permissions and limitations
|
|
|
|
under the License.
|
2014-11-04 16:51:32 +00:00
|
|
|
*/
|
2014-10-26 16:47:18 +01:00
|
|
|
|
|
|
|
package de.appplant.cordova.plugin.background;
|
|
|
|
|
2019-02-03 15:58:40 +01:00
|
|
|
import android.annotation.SuppressLint;
|
2017-02-06 13:34:27 +01:00
|
|
|
import android.annotation.TargetApi;
|
2019-02-04 11:18:34 -08:00
|
|
|
import android.app.*;
|
2014-10-26 16:47:18 +01:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.res.Resources;
|
2019-02-04 11:18:34 -08:00
|
|
|
import android.graphics.drawable.Icon;
|
2015-06-03 19:45:26 +01:00
|
|
|
import android.os.Binder;
|
2016-08-17 13:55:51 +02:00
|
|
|
import android.os.Build;
|
2014-10-26 16:47:18 +01:00
|
|
|
import android.os.IBinder;
|
2016-08-17 11:14:47 +02:00
|
|
|
import android.os.PowerManager;
|
|
|
|
import org.json.JSONObject;
|
2019-02-04 11:18:34 -08:00
|
|
|
import android.support.v4.app.NotificationCompat;
|
2016-08-17 11:14:47 +02:00
|
|
|
|
2017-02-06 13:34:27 +01:00
|
|
|
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
2016-08-17 13:55:51 +02:00
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
/**
|
|
|
|
* Puts the service in a foreground state, where the system considers it to be
|
|
|
|
* something the user is actively aware of and thus not a candidate for killing
|
|
|
|
* when low on memory.
|
|
|
|
*/
|
|
|
|
public class ForegroundService extends Service {
|
|
|
|
|
|
|
|
// Fixed ID for the 'foreground' notification
|
2016-08-17 11:52:31 +02:00
|
|
|
public static final int NOTIFICATION_ID = -574543954;
|
2014-10-26 16:47:18 +01:00
|
|
|
|
2017-01-23 10:12:42 +01:00
|
|
|
// Default title of the background notification
|
|
|
|
private static final String NOTIFICATION_TITLE =
|
|
|
|
"App is running in background";
|
|
|
|
|
|
|
|
// Default text of the background notification
|
|
|
|
private static final String NOTIFICATION_TEXT =
|
|
|
|
"Doing heavy tasks.";
|
|
|
|
|
|
|
|
// Default icon of the background notification
|
|
|
|
private static final String NOTIFICATION_ICON = "icon";
|
|
|
|
|
2015-06-03 19:45:26 +01:00
|
|
|
// Binder given to clients
|
2019-02-03 15:58:40 +01:00
|
|
|
private final IBinder binder = new ForegroundBinder();
|
2015-06-03 19:45:26 +01:00
|
|
|
|
2016-08-17 11:52:31 +02:00
|
|
|
// Partial wake lock to prevent the app from going to sleep when locked
|
2016-08-17 11:14:47 +02:00
|
|
|
private PowerManager.WakeLock wakeLock;
|
2014-10-26 16:47:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Allow clients to call on to the service.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public IBinder onBind (Intent intent) {
|
2019-02-03 15:58:40 +01:00
|
|
|
return binder;
|
2015-06-03 19:45:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class used for the client Binder. Because we know this service always
|
|
|
|
* runs in the same process as its clients, we don't need to deal with IPC.
|
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
class ForegroundBinder extends Binder
|
|
|
|
{
|
|
|
|
ForegroundService getService()
|
|
|
|
{
|
2016-08-17 11:52:31 +02:00
|
|
|
// Return this instance of ForegroundService
|
|
|
|
// so clients can call public methods
|
2015-06-03 19:45:26 +01:00
|
|
|
return ForegroundService.this;
|
|
|
|
}
|
2014-10-26 16:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Put the service in a foreground state to prevent app from being killed
|
|
|
|
* by the OS.
|
|
|
|
*/
|
|
|
|
@Override
|
2019-02-03 15:58:40 +01:00
|
|
|
public void onCreate()
|
|
|
|
{
|
2014-10-26 16:47:18 +01:00
|
|
|
super.onCreate();
|
|
|
|
keepAwake();
|
|
|
|
}
|
|
|
|
|
2016-08-17 11:14:47 +02:00
|
|
|
/**
|
|
|
|
* No need to run headless on destroy.
|
|
|
|
*/
|
2014-10-26 16:47:18 +01:00
|
|
|
@Override
|
2019-02-03 15:58:40 +01:00
|
|
|
public void onDestroy()
|
|
|
|
{
|
2014-10-26 16:47:18 +01:00
|
|
|
super.onDestroy();
|
|
|
|
sleepWell();
|
|
|
|
}
|
|
|
|
|
2019-02-03 15:58:40 +01:00
|
|
|
/**
|
|
|
|
* Prevent Android from stopping the background service automatically.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public int onStartCommand (Intent intent, int flags, int startId) {
|
|
|
|
return START_STICKY;
|
|
|
|
}
|
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
/**
|
|
|
|
* Put the service in a foreground state to prevent app from being killed
|
|
|
|
* by the OS.
|
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
@SuppressLint("WakelockTimeout")
|
|
|
|
private void keepAwake()
|
|
|
|
{
|
2016-08-17 11:52:31 +02:00
|
|
|
JSONObject settings = BackgroundMode.getSettings();
|
|
|
|
boolean isSilent = settings.optBoolean("silent", false);
|
2014-10-26 16:47:18 +01:00
|
|
|
|
2016-08-17 11:52:31 +02:00
|
|
|
if (!isSilent) {
|
2015-01-01 17:24:37 +01:00
|
|
|
startForeground(NOTIFICATION_ID, makeNotification());
|
|
|
|
}
|
2014-10-26 16:47:18 +01:00
|
|
|
|
2019-02-03 15:58:40 +01:00
|
|
|
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
|
2016-08-17 11:14:47 +02:00
|
|
|
|
2017-02-10 13:10:46 +01:00
|
|
|
wakeLock = pm.newWakeLock(
|
2019-02-03 15:58:40 +01:00
|
|
|
PARTIAL_WAKE_LOCK, "backgroundmode:wakelock");
|
2016-08-17 11:14:47 +02:00
|
|
|
|
|
|
|
wakeLock.acquire();
|
2014-10-26 16:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop background mode.
|
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
private void sleepWell()
|
|
|
|
{
|
2014-10-26 16:47:18 +01:00
|
|
|
stopForeground(true);
|
2017-01-01 22:35:32 +01:00
|
|
|
getNotificationManager().cancel(NOTIFICATION_ID);
|
2016-08-17 11:14:47 +02:00
|
|
|
|
|
|
|
if (wakeLock != null) {
|
|
|
|
wakeLock.release();
|
|
|
|
wakeLock = null;
|
|
|
|
}
|
2014-10-26 16:47:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a notification as the visible part to be able to put the service
|
2016-08-17 11:52:31 +02:00
|
|
|
* in a foreground state by using the default settings.
|
2014-10-26 16:47:18 +01:00
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
private Notification makeNotification()
|
|
|
|
{
|
2016-08-17 11:52:31 +02:00
|
|
|
return makeNotification(BackgroundMode.getSettings());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a notification as the visible part to be able to put the service
|
|
|
|
* in a foreground state.
|
|
|
|
*
|
2017-01-01 22:35:32 +01:00
|
|
|
* @param settings The config settings
|
2016-08-17 11:52:31 +02:00
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
private Notification makeNotification (JSONObject settings)
|
|
|
|
{
|
2019-01-31 18:26:29 +01:00
|
|
|
// use channelid for Oreo and higher
|
2019-02-04 11:18:34 -08:00
|
|
|
String CHANNEL_ID = "cordova-plugin-background-mode-id";
|
|
|
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
// The user-visible name of the channel.
|
|
|
|
CharSequence name = settings.optString("channelName", "cordova-plugin-background-mode");
|
|
|
|
// The user-visible description of the channel.
|
|
|
|
String description = settings.optString("channelDescription", "cordova-plugin-background-moden notification");
|
2019-01-31 18:26:29 +01:00
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
int importance = NotificationManager.IMPORTANCE_LOW;
|
2019-01-31 18:26:29 +01:00
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
|
2019-01-31 18:26:29 +01:00
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
// Configure the notification channel.
|
|
|
|
mChannel.setDescription(description);
|
2019-01-31 18:26:29 +01:00
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
getNotificationManager().createNotificationChannel(mChannel);
|
2019-01-31 18:26:29 +01:00
|
|
|
}
|
2017-01-23 10:12:42 +01:00
|
|
|
String title = settings.optString("title", NOTIFICATION_TITLE);
|
|
|
|
String text = settings.optString("text", NOTIFICATION_TEXT);
|
2017-01-01 22:35:32 +01:00
|
|
|
boolean bigText = settings.optBoolean("bigText", false);
|
2019-02-04 11:18:34 -08:00
|
|
|
String subText = settings.optString("subText", "");
|
2017-01-01 22:35:32 +01:00
|
|
|
|
2016-08-17 11:52:31 +02:00
|
|
|
Context context = getApplicationContext();
|
|
|
|
String pkgName = context.getPackageName();
|
|
|
|
Intent intent = context.getPackageManager()
|
2014-10-26 16:47:18 +01:00
|
|
|
.getLaunchIntentForPackage(pkgName);
|
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
NotificationCompat.Builder notification = new NotificationCompat.Builder(context, CHANNEL_ID)
|
2017-01-23 10:12:42 +01:00
|
|
|
.setContentTitle(title)
|
2017-01-01 22:35:32 +01:00
|
|
|
.setContentText(text)
|
2016-08-17 11:14:47 +02:00
|
|
|
.setOngoing(true)
|
2017-01-16 15:18:19 +01:00
|
|
|
.setSmallIcon(getIconResId(settings));
|
2015-06-03 16:33:09 +01:00
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
if (!subText.equals("")) {
|
|
|
|
notification.setSubText(subText);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (settings.optBoolean("allowClose", false)) {
|
|
|
|
|
|
|
|
final Intent clostAppIntent = new Intent("com.backgroundmode.close");
|
|
|
|
final PendingIntent closeIntent = PendingIntent.getBroadcast(context, 1337, clostAppIntent, 0);
|
|
|
|
final String closeIconName = settings.optString("closeIcon", "power");
|
|
|
|
NotificationCompat.Action.Builder closeAction = new NotificationCompat.Action.Builder(getIconResId(closeIconName), settings.optString("closeTitle", "Close"), closeIntent);
|
|
|
|
notification.addAction(closeAction.build());
|
2019-01-31 18:26:29 +01:00
|
|
|
}
|
|
|
|
|
2017-01-26 14:45:01 +01:00
|
|
|
if (settings.optBoolean("hidden", true)) {
|
|
|
|
notification.setPriority(Notification.PRIORITY_MIN);
|
|
|
|
}
|
|
|
|
|
2017-01-01 22:35:32 +01:00
|
|
|
if (bigText || text.contains("\n")) {
|
|
|
|
notification.setStyle(
|
2019-02-04 11:18:34 -08:00
|
|
|
new NotificationCompat.BigTextStyle().bigText(text));
|
2017-01-01 22:35:32 +01:00
|
|
|
}
|
|
|
|
|
2016-08-17 13:55:51 +02:00
|
|
|
setColor(notification, settings);
|
|
|
|
|
2014-11-04 16:51:32 +00:00
|
|
|
if (intent != null && settings.optBoolean("resume")) {
|
2017-06-14 22:00:38 +02:00
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
2014-11-04 16:51:32 +00:00
|
|
|
PendingIntent contentIntent = PendingIntent.getActivity(
|
2016-08-17 11:14:47 +02:00
|
|
|
context, NOTIFICATION_ID, intent,
|
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
2014-11-04 14:48:14 +00:00
|
|
|
|
2017-02-06 13:34:27 +01:00
|
|
|
|
2014-11-04 14:48:14 +00:00
|
|
|
notification.setContentIntent(contentIntent);
|
|
|
|
}
|
2014-10-26 16:47:18 +01:00
|
|
|
|
2016-08-17 11:14:47 +02:00
|
|
|
return notification.build();
|
2014-10-26 16:47:18 +01:00
|
|
|
}
|
|
|
|
|
2016-08-17 11:14:47 +02:00
|
|
|
/**
|
|
|
|
* Update the notification.
|
2016-08-17 11:52:31 +02:00
|
|
|
*
|
2017-01-01 22:35:32 +01:00
|
|
|
* @param settings The config settings
|
2016-08-17 11:14:47 +02:00
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
protected void updateNotification (JSONObject settings)
|
|
|
|
{
|
2016-08-17 11:52:31 +02:00
|
|
|
boolean isSilent = settings.optBoolean("silent", false);
|
|
|
|
|
|
|
|
if (isSilent) {
|
|
|
|
stopForeground(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-01 22:35:32 +01:00
|
|
|
Notification notification = makeNotification(settings);
|
2017-02-06 13:34:27 +01:00
|
|
|
getNotificationManager().notify(NOTIFICATION_ID, notification);
|
2019-01-31 18:26:29 +01:00
|
|
|
|
2015-06-03 19:45:26 +01:00
|
|
|
}
|
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
/**
|
2019-02-04 11:18:34 -08:00
|
|
|
* Retrieves the resource ID of the sent icon name
|
2017-01-16 15:18:19 +01:00
|
|
|
*
|
2019-02-04 11:18:34 -08:00
|
|
|
* @param name Name of the resource to return
|
2014-10-26 16:47:18 +01:00
|
|
|
*/
|
2019-02-04 11:18:34 -08:00
|
|
|
private int getIconResId(String name) {
|
|
|
|
int resId = getIconResId(name, "mipmap");
|
|
|
|
|
|
|
|
if (resId == 0) {
|
|
|
|
resId = getIconResId(name, "drawable");
|
|
|
|
}
|
2016-08-17 13:26:47 +02:00
|
|
|
|
|
|
|
if (resId == 0) {
|
2019-02-04 11:18:34 -08:00
|
|
|
resId = getIconResId("icon", "mipmap");
|
2016-12-31 13:36:30 +01:00
|
|
|
}
|
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
if (resId == 0) {
|
|
|
|
resId = getIconResId("icon", "drawable");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-31 13:36:30 +01:00
|
|
|
return resId;
|
|
|
|
}
|
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
/**
|
|
|
|
* Retrieves the resource ID of the app icon.
|
|
|
|
*
|
|
|
|
* @param settings A JSON dict containing the icon name.
|
|
|
|
*/
|
|
|
|
private int getIconResId(JSONObject settings) {
|
|
|
|
String icon = settings.optString("icon", NOTIFICATION_ICON);
|
|
|
|
|
|
|
|
return getIconResId(icon);
|
|
|
|
}
|
|
|
|
|
2016-12-31 13:36:30 +01:00
|
|
|
/**
|
|
|
|
* Retrieve resource id of the specified icon.
|
|
|
|
*
|
2017-01-01 22:35:32 +01:00
|
|
|
* @param icon The name of the icon.
|
|
|
|
* @param type The resource type where to look for.
|
2016-12-31 13:36:30 +01:00
|
|
|
*
|
|
|
|
* @return The resource id or 0 if not found.
|
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
private int getIconResId (String icon, String type)
|
|
|
|
{
|
2017-02-07 09:12:04 +01:00
|
|
|
Resources res = getResources();
|
|
|
|
String pkgName = getPackageName();
|
2016-12-31 13:36:30 +01:00
|
|
|
|
2019-02-04 11:18:34 -08:00
|
|
|
return res.getIdentifier(icon, type, pkgName);
|
2014-10-26 16:47:18 +01:00
|
|
|
}
|
2016-08-17 13:55:51 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set notification color if its supported by the SDK.
|
|
|
|
*
|
2017-01-01 22:35:32 +01:00
|
|
|
* @param notification A Notification.Builder instance
|
|
|
|
* @param settings A JSON dict containing the color definition (red: FF0000)
|
2016-08-17 13:55:51 +02:00
|
|
|
*/
|
2017-02-06 13:34:27 +01:00
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
2019-02-04 11:18:34 -08:00
|
|
|
private void setColor(NotificationCompat.Builder notification,
|
2016-08-17 13:55:51 +02:00
|
|
|
JSONObject settings) {
|
|
|
|
|
|
|
|
String hex = settings.optString("color", null);
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT < 21 || hex == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
try {
|
2017-01-16 10:17:25 +01:00
|
|
|
int aRGB = Integer.parseInt(hex, 16) + 0xFF000000;
|
2017-02-06 13:34:27 +01:00
|
|
|
notification.setColor(aRGB);
|
2017-01-16 10:17:25 +01:00
|
|
|
} catch (Exception e) {
|
2016-08-17 13:55:51 +02:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
2017-01-01 22:35:32 +01:00
|
|
|
|
|
|
|
/**
|
2019-02-03 15:58:40 +01:00
|
|
|
* Returns the shared notification service manager.
|
2017-01-01 22:35:32 +01:00
|
|
|
*/
|
2019-02-03 15:58:40 +01:00
|
|
|
private NotificationManager getNotificationManager()
|
|
|
|
{
|
2017-02-06 13:34:27 +01:00
|
|
|
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
2017-01-01 22:35:32 +01:00
|
|
|
}
|
|
|
|
|
2014-10-26 16:47:18 +01:00
|
|
|
}
|