Home | History | Annotate | Download | only in app
      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.Handler;
     27 import android.os.IBinder;
     28 import android.os.PowerManager;
     29 import android.util.Log;
     30 import android.view.View;
     31 import android.view.View.OnClickListener;
     32 import android.widget.Button;
     33 
     34 import java.lang.reflect.InvocationTargetException;
     35 import java.lang.reflect.Method;
     36 
     37 // Need the following import to get access to the app resources, since this
     38 // class is in a sub-package.
     39 import com.example.android.apis.R;
     40 
     41 /**
     42  * This is an example of implementing an application service that can
     43  * run in the "foreground".  It shows how to code this to work well by using
     44  * the improved Android 2.0 APIs when available and otherwise falling back
     45  * to the original APIs.  Yes: you can take this exact code, compile it
     46  * against the Android 2.0 SDK, and it will run against everything down to
     47  * Android 1.0.
     48  */
     49 public class ForegroundService extends Service {
     50     static final String ACTION_FOREGROUND = "com.example.android.apis.FOREGROUND";
     51     static final String ACTION_FOREGROUND_WAKELOCK = "com.example.android.apis.FOREGROUND_WAKELOCK";
     52     static final String ACTION_BACKGROUND = "com.example.android.apis.BACKGROUND";
     53     static final String ACTION_BACKGROUND_WAKELOCK = "com.example.android.apis.BACKGROUND_WAKELOCK";
     54 
     55  // BEGIN_INCLUDE(foreground_compatibility)
     56     private static final Class<?>[] mSetForegroundSignature = new Class[] {
     57         boolean.class};
     58     private static final Class<?>[] mStartForegroundSignature = new Class[] {
     59         int.class, Notification.class};
     60     private static final Class<?>[] mStopForegroundSignature = new Class[] {
     61         boolean.class};
     62 
     63     private NotificationManager mNM;
     64     private Method mSetForeground;
     65     private Method mStartForeground;
     66     private Method mStopForeground;
     67     private Object[] mSetForegroundArgs = new Object[1];
     68     private Object[] mStartForegroundArgs = new Object[2];
     69     private Object[] mStopForegroundArgs = new Object[1];
     70 
     71     void invokeMethod(Method method, Object[] args) {
     72         try {
     73             method.invoke(this, args);
     74         } catch (InvocationTargetException e) {
     75             // Should not happen.
     76             Log.w("ApiDemos", "Unable to invoke method", e);
     77         } catch (IllegalAccessException e) {
     78             // Should not happen.
     79             Log.w("ApiDemos", "Unable to invoke method", e);
     80         }
     81     }
     82 
     83     /**
     84      * This is a wrapper around the new startForeground method, using the older
     85      * APIs if it is not available.
     86      */
     87     void startForegroundCompat(int id, Notification notification) {
     88         // If we have the new startForeground API, then use it.
     89         if (mStartForeground != null) {
     90             mStartForegroundArgs[0] = Integer.valueOf(id);
     91             mStartForegroundArgs[1] = notification;
     92             invokeMethod(mStartForeground, mStartForegroundArgs);
     93             return;
     94         }
     95 
     96         // Fall back on the old API.
     97         mSetForegroundArgs[0] = Boolean.TRUE;
     98         invokeMethod(mSetForeground, mSetForegroundArgs);
     99         mNM.notify(id, notification);
    100     }
    101 
    102     /**
    103      * This is a wrapper around the new stopForeground method, using the older
    104      * APIs if it is not available.
    105      */
    106     void stopForegroundCompat(int id) {
    107         // If we have the new stopForeground API, then use it.
    108         if (mStopForeground != null) {
    109             mStopForegroundArgs[0] = Boolean.TRUE;
    110             invokeMethod(mStopForeground, mStopForegroundArgs);
    111             return;
    112         }
    113 
    114         // Fall back on the old API.  Note to cancel BEFORE changing the
    115         // foreground state, since we could be killed at that point.
    116         mNM.cancel(id);
    117         mSetForegroundArgs[0] = Boolean.FALSE;
    118         invokeMethod(mSetForeground, mSetForegroundArgs);
    119     }
    120 
    121     @Override
    122     public void onCreate() {
    123         mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    124         try {
    125             mStartForeground = getClass().getMethod("startForeground",
    126                     mStartForegroundSignature);
    127             mStopForeground = getClass().getMethod("stopForeground",
    128                     mStopForegroundSignature);
    129             return;
    130         } catch (NoSuchMethodException e) {
    131             // Running on an older platform.
    132             mStartForeground = mStopForeground = null;
    133         }
    134         try {
    135             mSetForeground = getClass().getMethod("setForeground",
    136                     mSetForegroundSignature);
    137         } catch (NoSuchMethodException e) {
    138             throw new IllegalStateException(
    139                     "OS doesn't have Service.startForeground OR Service.setForeground!");
    140         }
    141     }
    142 
    143     @Override
    144     public void onDestroy() {
    145         handleDestroy();
    146         // Make sure our notification is gone.
    147         stopForegroundCompat(R.string.foreground_service_started);
    148     }
    149 // END_INCLUDE(foreground_compatibility)
    150 
    151 // BEGIN_INCLUDE(start_compatibility)
    152     // This is the old onStart method that will be called on the pre-2.0
    153     // platform.  On 2.0 or later we override onStartCommand() so this
    154     // method will not be called.
    155     @Override
    156     public void onStart(Intent intent, int startId) {
    157         handleCommand(intent);
    158     }
    159 
    160     @Override
    161     public int onStartCommand(Intent intent, int flags, int startId) {
    162         handleCommand(intent);
    163         // We want this service to continue running until it is explicitly
    164         // stopped, so return sticky.
    165         return START_STICKY;
    166     }
    167 // END_INCLUDE(start_compatibility)
    168 
    169     private PowerManager.WakeLock mWakeLock;
    170     private Handler mHandler = new Handler();
    171     private Runnable mPulser = new Runnable() {
    172         @Override public void run() {
    173             Log.i("ForegroundService", "PULSE!");
    174             mHandler.postDelayed(this, 5*1000);
    175         }
    176     };
    177 
    178     void handleCommand(Intent intent) {
    179         if (ACTION_FOREGROUND.equals(intent.getAction())
    180                 || ACTION_FOREGROUND_WAKELOCK.equals(intent.getAction())) {
    181             // In this sample, we'll use the same text for the ticker and the expanded notification
    182             CharSequence text = getText(R.string.foreground_service_started);
    183 
    184             PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
    185                     new Intent(this, Controller.class), 0);
    186 
    187             // Set the info for the views that show in the notification panel.
    188             Notification notification = new Notification.Builder(this)
    189                     .setSmallIcon(R.drawable.stat_sample)  // the status icon
    190                     .setTicker(text)  // the status text
    191                     .setWhen(System.currentTimeMillis())  // the time stamp
    192                     .setContentTitle(getText(R.string.alarm_service_label))  // the label
    193                     .setContentText(text)  // the contents of the entry
    194                     .setContentIntent(contentIntent)  // The intent to send when clicked
    195                     .build();
    196 
    197             startForegroundCompat(R.string.foreground_service_started, notification);
    198 
    199         } else if (ACTION_BACKGROUND.equals(intent.getAction())
    200                 || ACTION_BACKGROUND_WAKELOCK.equals(intent.getAction())) {
    201             stopForegroundCompat(R.string.foreground_service_started);
    202         }
    203 
    204         if (ACTION_FOREGROUND_WAKELOCK.equals(intent.getAction())
    205                 || ACTION_BACKGROUND_WAKELOCK.equals(intent.getAction())) {
    206             if (mWakeLock == null) {
    207                 mWakeLock = getSystemService(PowerManager.class).newWakeLock(
    208                         PowerManager.PARTIAL_WAKE_LOCK, "wake-service");
    209                 mWakeLock.acquire();
    210             } else {
    211                 releaseWakeLock();
    212             }
    213         }
    214 
    215         mHandler.removeCallbacks(mPulser);
    216         mPulser.run();
    217     }
    218 
    219     void releaseWakeLock() {
    220         if (mWakeLock != null) {
    221             mWakeLock.release();
    222             mWakeLock = null;
    223         }
    224     }
    225 
    226     void handleDestroy() {
    227         releaseWakeLock();
    228         mHandler.removeCallbacks(mPulser);
    229     }
    230 
    231     @Override
    232     public IBinder onBind(Intent intent) {
    233         return null;
    234     }
    235 
    236     // ----------------------------------------------------------------------
    237 
    238     /**
    239      * <p>Example of explicitly starting and stopping the {@link ForegroundService}.
    240      *
    241      * <p>Note that this is implemented as an inner class only keep the sample
    242      * all together; typically this code would appear in some separate class.
    243      */
    244     public static class Controller extends Activity {
    245         @Override
    246         protected void onCreate(Bundle savedInstanceState) {
    247             super.onCreate(savedInstanceState);
    248 
    249             setContentView(R.layout.foreground_service_controller);
    250 
    251             // Watch for button clicks.
    252             Button button = (Button)findViewById(R.id.start_foreground);
    253             button.setOnClickListener(mForegroundListener);
    254             button = (Button)findViewById(R.id.start_foreground_wakelock);
    255             button.setOnClickListener(mForegroundWakelockListener);
    256             button = (Button)findViewById(R.id.start_background);
    257             button.setOnClickListener(mBackgroundListener);
    258             button = (Button)findViewById(R.id.start_background_wakelock);
    259             button.setOnClickListener(mBackgroundWakelockListener);
    260             button = (Button)findViewById(R.id.stop);
    261             button.setOnClickListener(mStopListener);
    262             button = (Button)findViewById(R.id.start_foreground_2);
    263             button.setOnClickListener(mForegroundListener2);
    264             button = (Button)findViewById(R.id.stop_2);
    265             button.setOnClickListener(mStopListener2);
    266         }
    267 
    268         private OnClickListener mForegroundListener = new OnClickListener() {
    269             public void onClick(View v) {
    270                 Intent intent = new Intent(ForegroundService.ACTION_FOREGROUND);
    271                 intent.setClass(Controller.this, ForegroundService.class);
    272                 startService(intent);
    273             }
    274         };
    275 
    276         private OnClickListener mForegroundWakelockListener = new OnClickListener() {
    277             public void onClick(View v) {
    278                 Intent intent = new Intent(ForegroundService.ACTION_FOREGROUND_WAKELOCK);
    279                 intent.setClass(Controller.this, ForegroundService.class);
    280                 startService(intent);
    281             }
    282         };
    283 
    284         private OnClickListener mBackgroundListener = new OnClickListener() {
    285             public void onClick(View v) {
    286                 Intent intent = new Intent(ForegroundService.ACTION_BACKGROUND);
    287                 intent.setClass(Controller.this, ForegroundService.class);
    288                 startService(intent);
    289             }
    290         };
    291 
    292         private OnClickListener mBackgroundWakelockListener = new OnClickListener() {
    293             public void onClick(View v) {
    294                 Intent intent = new Intent(ForegroundService.ACTION_BACKGROUND_WAKELOCK);
    295                 intent.setClass(Controller.this, ForegroundService.class);
    296                 startService(intent);
    297             }
    298         };
    299 
    300         private OnClickListener mStopListener = new OnClickListener() {
    301             public void onClick(View v) {
    302                 stopService(new Intent(Controller.this,
    303                         ForegroundService.class));
    304             }
    305         };
    306 
    307         private OnClickListener mForegroundListener2 = new OnClickListener() {
    308             public void onClick(View v) {
    309                 Intent intent = new Intent(ForegroundService.ACTION_FOREGROUND);
    310                 intent.setClass(Controller.this, ForegroundService2.class);
    311                 startService(intent);
    312             }
    313         };
    314 
    315         private OnClickListener mStopListener2 = new OnClickListener() {
    316             public void onClick(View v) {
    317                 stopService(new Intent(Controller.this,
    318                         ForegroundService2.class));
    319             }
    320         };
    321 
    322     }
    323 }
    324