Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright (C) 2017 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.googlecode.android_scripting.service;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.Notification;
     21 import android.app.NotificationChannel;
     22 import android.app.NotificationManager;
     23 import android.app.PendingIntent;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.os.Binder;
     27 import android.os.IBinder;
     28 
     29 import com.google.common.base.Preconditions;
     30 import com.googlecode.android_scripting.BaseApplication;
     31 import com.googlecode.android_scripting.ForegroundService;
     32 import com.googlecode.android_scripting.IntentBuilders;
     33 import com.googlecode.android_scripting.NotificationIdFactory;
     34 import com.googlecode.android_scripting.R;
     35 import com.googlecode.android_scripting.activity.TriggerManager;
     36 import com.googlecode.android_scripting.event.Event;
     37 import com.googlecode.android_scripting.event.EventObserver;
     38 import com.googlecode.android_scripting.facade.EventFacade;
     39 import com.googlecode.android_scripting.facade.FacadeConfiguration;
     40 import com.googlecode.android_scripting.facade.FacadeManager;
     41 import com.googlecode.android_scripting.trigger.EventGenerationControllingObserver;
     42 import com.googlecode.android_scripting.trigger.Trigger;
     43 import com.googlecode.android_scripting.trigger.TriggerRepository;
     44 import com.googlecode.android_scripting.trigger.TriggerRepository.TriggerRepositoryObserver;
     45 
     46 /**
     47  * The trigger service takes care of installing triggers serialized to the preference storage.
     48  *
     49  * <p>
     50  * The service also installs an alarm that keeps it running, unless the user force-quits the
     51  * service.
     52  *
     53  * <p>
     54  * When no triggers are installed the service shuts down silently as to not consume resources
     55  * unnecessarily.
     56  *
     57  */
     58 public class TriggerService extends ForegroundService {
     59   private static final String CHANNEL_ID = "trigger_service_channel";
     60   private static final int NOTIFICATION_ID = NotificationIdFactory.create();
     61   private static final long PING_MILLIS = 10 * 1000 * 60;
     62 
     63   private final IBinder mBinder;
     64   private TriggerRepository mTriggerRepository;
     65   private FacadeManager mFacadeManager;
     66   private EventFacade mEventFacade;
     67 
     68   public class LocalBinder extends Binder {
     69     public TriggerService getService() {
     70       return TriggerService.this;
     71     }
     72   }
     73 
     74   public TriggerService() {
     75     super(NOTIFICATION_ID);
     76     mBinder = new LocalBinder();
     77   }
     78 
     79   @Override
     80   public IBinder onBind(Intent intent) {
     81     return mBinder;
     82   }
     83 
     84   @Override
     85   public void onCreate() {
     86     super.onCreate();
     87 
     88     mFacadeManager =
     89         new FacadeManager(FacadeConfiguration.getSdkLevel(), this, null,
     90             FacadeConfiguration.getFacadeClasses());
     91     mEventFacade = mFacadeManager.getReceiver(EventFacade.class);
     92 
     93     mTriggerRepository = ((BaseApplication) getApplication()).getTriggerRepository();
     94     mTriggerRepository.bootstrapObserver(new RepositoryObserver());
     95     mTriggerRepository.bootstrapObserver(new EventGenerationControllingObserver(mFacadeManager));
     96     installAlarm();
     97   }
     98 
     99   @Override
    100   public void onStart(Intent intent, int startId) {
    101     if (mTriggerRepository.isEmpty()) {
    102       stopSelfResult(startId);
    103       return;
    104     }
    105   }
    106 
    107   protected void createNotificationChannel() {
    108     NotificationManager notificationManager = getNotificationManager();
    109     CharSequence name = getString(R.string.notification_channel_name);
    110     String description = getString(R.string.notification_channel_description);
    111     int importance = NotificationManager.IMPORTANCE_DEFAULT;
    112     NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
    113     channel.setDescription(description);
    114     channel.enableLights(false);
    115     channel.enableVibration(false);
    116     notificationManager.createNotificationChannel(channel);
    117   }
    118 
    119   /** Returns the notification to display whenever the service is running. */
    120   @Override
    121   protected Notification createNotification() {
    122     createNotificationChannel();
    123     Intent notificationIntent = new Intent(this, TriggerManager.class);
    124     Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID);
    125     builder.setSmallIcon(R.drawable.sl4a_logo_48)
    126            .setTicker("SL4A Trigger Service started.")
    127            .setWhen(System.currentTimeMillis())
    128            .setContentTitle("SL4A Trigger Service")
    129            .setContentText("Tap to view triggers")
    130            .setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
    131     Notification notification = builder.build();
    132     notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
    133     return notification;
    134   }
    135 
    136   private class TriggerEventObserver implements EventObserver {
    137     private final Trigger mTrigger;
    138 
    139     public TriggerEventObserver(Trigger trigger) {
    140       mTrigger = trigger;
    141     }
    142 
    143     @Override
    144     public void onEventReceived(Event event) {
    145       mTrigger.handleEvent(event, TriggerService.this);
    146     }
    147   }
    148 
    149   private class RepositoryObserver implements TriggerRepositoryObserver {
    150     int mTriggerCount = 0;
    151 
    152     @Override
    153     public void onPut(Trigger trigger) {
    154       mTriggerCount++;
    155       mEventFacade.addNamedEventObserver(trigger.getEventName(), new TriggerEventObserver(trigger));
    156     }
    157 
    158     @Override
    159     public void onRemove(Trigger trigger) {
    160       Preconditions.checkArgument(mTriggerCount > 0);
    161       // TODO(damonkohler): Tear down EventObserver associated with trigger.
    162       if (--mTriggerCount == 0) {
    163         // TODO(damonkohler): Use stopSelfResult() which would require tracking startId.
    164         stopSelf();
    165       }
    166     }
    167   }
    168 
    169   private void installAlarm() {
    170     AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    171     alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + PING_MILLIS,
    172         PING_MILLIS, IntentBuilders.buildTriggerServicePendingIntent(this));
    173   }
    174 
    175   private void uninstallAlarm() {
    176     AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    177     alarmManager.cancel(IntentBuilders.buildTriggerServicePendingIntent(this));
    178   }
    179 
    180   @Override
    181   public void onDestroy() {
    182     super.onDestroy();
    183     uninstallAlarm();
    184   }
    185 }
    186