1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.apis.app; 18 19 import android.app.Activity; 20 import android.app.Notification; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.app.Service; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.os.IBinder; 27 import android.util.Log; 28 import android.view.View; 29 import android.view.View.OnClickListener; 30 import android.widget.Button; 31 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 35 // Need the following import to get access to the app resources, since this 36 // class is in a sub-package. 37 import com.example.android.apis.R; 38 39 /** 40 * This is an example of implementing an application service that can 41 * run in the "foreground". It shows how to code this to work well by using 42 * the improved Android 2.0 APIs when available and otherwise falling back 43 * to the original APIs. Yes: you can take this exact code, compile it 44 * against the Android 2.0 SDK, and it will against everything down to 45 * Android 1.0. 46 */ 47 public class ForegroundService extends Service { 48 static final String ACTION_FOREGROUND = "com.example.android.apis.FOREGROUND"; 49 static final String ACTION_BACKGROUND = "com.example.android.apis.BACKGROUND"; 50 51 // BEGIN_INCLUDE(foreground_compatibility) 52 private static final Class<?>[] mSetForegroundSignature = new Class[] { 53 boolean.class}; 54 private static final Class<?>[] mStartForegroundSignature = new Class[] { 55 int.class, Notification.class}; 56 private static final Class<?>[] mStopForegroundSignature = new Class[] { 57 boolean.class}; 58 59 private NotificationManager mNM; 60 private Method mSetForeground; 61 private Method mStartForeground; 62 private Method mStopForeground; 63 private Object[] mSetForegroundArgs = new Object[1]; 64 private Object[] mStartForegroundArgs = new Object[2]; 65 private Object[] mStopForegroundArgs = new Object[1]; 66 67 void invokeMethod(Method method, Object[] args) { 68 try { 69 method.invoke(this, args); 70 } catch (InvocationTargetException e) { 71 // Should not happen. 72 Log.w("ApiDemos", "Unable to invoke method", e); 73 } catch (IllegalAccessException e) { 74 // Should not happen. 75 Log.w("ApiDemos", "Unable to invoke method", e); 76 } 77 } 78 79 /** 80 * This is a wrapper around the new startForeground method, using the older 81 * APIs if it is not available. 82 */ 83 void startForegroundCompat(int id, Notification notification) { 84 // If we have the new startForeground API, then use it. 85 if (mStartForeground != null) { 86 mStartForegroundArgs[0] = Integer.valueOf(id); 87 mStartForegroundArgs[1] = notification; 88 invokeMethod(mStartForeground, mStartForegroundArgs); 89 return; 90 } 91 92 // Fall back on the old API. 93 mSetForegroundArgs[0] = Boolean.TRUE; 94 invokeMethod(mSetForeground, mSetForegroundArgs); 95 mNM.notify(id, notification); 96 } 97 98 /** 99 * This is a wrapper around the new stopForeground method, using the older 100 * APIs if it is not available. 101 */ 102 void stopForegroundCompat(int id) { 103 // If we have the new stopForeground API, then use it. 104 if (mStopForeground != null) { 105 mStopForegroundArgs[0] = Boolean.TRUE; 106 invokeMethod(mStopForeground, mStopForegroundArgs); 107 return; 108 } 109 110 // Fall back on the old API. Note to cancel BEFORE changing the 111 // foreground state, since we could be killed at that point. 112 mNM.cancel(id); 113 mSetForegroundArgs[0] = Boolean.FALSE; 114 invokeMethod(mSetForeground, mSetForegroundArgs); 115 } 116 117 @Override 118 public void onCreate() { 119 mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); 120 try { 121 mStartForeground = getClass().getMethod("startForeground", 122 mStartForegroundSignature); 123 mStopForeground = getClass().getMethod("stopForeground", 124 mStopForegroundSignature); 125 return; 126 } catch (NoSuchMethodException e) { 127 // Running on an older platform. 128 mStartForeground = mStopForeground = null; 129 } 130 try { 131 mSetForeground = getClass().getMethod("setForeground", 132 mSetForegroundSignature); 133 } catch (NoSuchMethodException e) { 134 throw new IllegalStateException( 135 "OS doesn't have Service.startForeground OR Service.setForeground!"); 136 } 137 } 138 139 @Override 140 public void onDestroy() { 141 // Make sure our notification is gone. 142 stopForegroundCompat(R.string.foreground_service_started); 143 } 144 // END_INCLUDE(foreground_compatibility) 145 146 // BEGIN_INCLUDE(start_compatibility) 147 // This is the old onStart method that will be called on the pre-2.0 148 // platform. On 2.0 or later we override onStartCommand() so this 149 // method will not be called. 150 @Override 151 public void onStart(Intent intent, int startId) { 152 handleCommand(intent); 153 } 154 155 @Override 156 public int onStartCommand(Intent intent, int flags, int startId) { 157 handleCommand(intent); 158 // We want this service to continue running until it is explicitly 159 // stopped, so return sticky. 160 return START_STICKY; 161 } 162 // END_INCLUDE(start_compatibility) 163 164 void handleCommand(Intent intent) { 165 if (ACTION_FOREGROUND.equals(intent.getAction())) { 166 // In this sample, we'll use the same text for the ticker and the expanded notification 167 CharSequence text = getText(R.string.foreground_service_started); 168 169 // Set the icon, scrolling text and timestamp 170 Notification notification = new Notification(R.drawable.stat_sample, text, 171 System.currentTimeMillis()); 172 173 // The PendingIntent to launch our activity if the user selects this notification 174 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 175 new Intent(this, Controller.class), 0); 176 177 // Set the info for the views that show in the notification panel. 178 notification.setLatestEventInfo(this, getText(R.string.local_service_label), 179 text, contentIntent); 180 181 startForegroundCompat(R.string.foreground_service_started, notification); 182 183 } else if (ACTION_BACKGROUND.equals(intent.getAction())) { 184 stopForegroundCompat(R.string.foreground_service_started); 185 } 186 } 187 188 @Override 189 public IBinder onBind(Intent intent) { 190 return null; 191 } 192 193 // ---------------------------------------------------------------------- 194 195 /** 196 * <p>Example of explicitly starting and stopping the {@link ForegroundService}. 197 * 198 * <p>Note that this is implemented as an inner class only keep the sample 199 * all together; typically this code would appear in some separate class. 200 */ 201 public static class Controller extends Activity { 202 @Override 203 protected void onCreate(Bundle savedInstanceState) { 204 super.onCreate(savedInstanceState); 205 206 setContentView(R.layout.foreground_service_controller); 207 208 // Watch for button clicks. 209 Button button = (Button)findViewById(R.id.start_foreground); 210 button.setOnClickListener(mForegroundListener); 211 button = (Button)findViewById(R.id.start_background); 212 button.setOnClickListener(mBackgroundListener); 213 button = (Button)findViewById(R.id.stop); 214 button.setOnClickListener(mStopListener); 215 } 216 217 private OnClickListener mForegroundListener = new OnClickListener() { 218 public void onClick(View v) { 219 Intent intent = new Intent(ForegroundService.ACTION_FOREGROUND); 220 intent.setClass(Controller.this, ForegroundService.class); 221 startService(intent); 222 } 223 }; 224 225 private OnClickListener mBackgroundListener = new OnClickListener() { 226 public void onClick(View v) { 227 Intent intent = new Intent(ForegroundService.ACTION_BACKGROUND); 228 intent.setClass(Controller.this, ForegroundService.class); 229 startService(intent); 230 } 231 }; 232 233 private OnClickListener mStopListener = new OnClickListener() { 234 public void onClick(View v) { 235 stopService(new Intent(Controller.this, 236 ForegroundService.class)); 237 } 238 }; 239 } 240 } 241