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