1 /*
2  * Copyright (C) 2016 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.googlecode.android_scripting;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.Service;
22 
23 import java.lang.reflect.Method;
24 
25 public abstract class ForegroundService extends Service {
26   private static final Class<?>[] mStartForegroundSignature =
27       new Class[] { int.class, Notification.class };
28   private static final Class<?>[] mStopForegroundSignature = new Class[] { boolean.class };
29 
30   private final int mNotificationId;
31 
32   private NotificationManager mNotificationManager;
33   private Method mStartForeground;
34   private Method mStopForeground;
35   private Object[] mStartForegroundArgs = new Object[2];
36   private Object[] mStopForegroundArgs = new Object[1];
37 
ForegroundService(int id)38   public ForegroundService(int id) {
39     mNotificationId = id;
40   }
41 
createNotification()42   protected abstract Notification createNotification();
43 
44   /**
45    * This is a wrapper around the new startForeground method, using the older APIs if it is not
46    * available.
47    */
startForegroundCompat(Notification notification)48   private void startForegroundCompat(Notification notification) {
49     // If we have the new startForeground API, then use it.
50     if (mStartForeground != null) {
51       mStartForegroundArgs[0] = Integer.valueOf(mNotificationId);
52       mStartForegroundArgs[1] = notification;
53       try {
54         mStartForeground.invoke(this, mStartForegroundArgs);
55       } catch (Exception e) {
56         Log.e(e);
57       }
58       return;
59     }
60 
61     // Fall back on the old API.
62     setForeground(true);
63     if (notification != null) {
64       mNotificationManager.notify(mNotificationId, notification);
65     }
66   }
67 
68   /**
69    * This is a wrapper around the new stopForeground method, using the older APIs if it is not
70    * available.
71    */
stopForegroundCompat()72   private void stopForegroundCompat() {
73     // If we have the new stopForeground API, then use it.
74     if (mStopForeground != null) {
75       mStopForegroundArgs[0] = Boolean.TRUE;
76       try {
77         mStopForeground.invoke(this, mStopForegroundArgs);
78       } catch (Exception e) {
79         Log.e(e);
80       }
81       return;
82     }
83 
84     // Fall back on the old API. Note to cancel BEFORE changing the
85     // foreground state, since we could be killed at that point.
86     mNotificationManager.cancel(mNotificationId);
87     setForeground(false);
88   }
89 
90   @Override
onCreate()91   public void onCreate() {
92     mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
93     try {
94       mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature);
95       mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
96     } catch (NoSuchMethodException e) {
97       // Running on an older platform.
98       mStartForeground = mStopForeground = null;
99     }
100     startForegroundCompat(createNotification());
101   }
102 
103   @Override
onDestroy()104   public void onDestroy() {
105     // Make sure our notification is gone.
106     stopForegroundCompat();
107   }
108 }
109