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  */
     55 public class TriggerService extends ForegroundService {
     56   private static final int NOTIFICATION_ID = NotificationIdFactory.create();
     57   private static final long PING_MILLIS = 10 * 1000 * 60;
     58 
     59   private final IBinder mBinder;
     60   private TriggerRepository mTriggerRepository;
     61   private FacadeManager mFacadeManager;
     62   private EventFacade mEventFacade;
     63 
     64   public class LocalBinder extends Binder {
     65     public TriggerService getService() {
     66       return TriggerService.this;
     67     }
     68   }
     69 
     70   public TriggerService() {
     71     super(NOTIFICATION_ID);
     72     mBinder = new LocalBinder();
     73   }
     74 
     75   @Override
     76   public IBinder onBind(Intent intent) {
     77     return mBinder;
     78   }
     79 
     80   @Override
     81   public void onCreate() {
     82     super.onCreate();
     83 
     84     mFacadeManager =
     85         new FacadeManager(FacadeConfiguration.getSdkLevel(), this, null,
     86             FacadeConfiguration.getFacadeClasses());
     87     mEventFacade = mFacadeManager.getReceiver(EventFacade.class);
     88 
     89     mTriggerRepository = ((BaseApplication) getApplication()).getTriggerRepository();
     90     mTriggerRepository.bootstrapObserver(new RepositoryObserver());
     91     mTriggerRepository.bootstrapObserver(new EventGenerationControllingObserver(mFacadeManager));
     92     installAlarm();
     93   }
     94 
     95   @Override
     96   public void onStart(Intent intent, int startId) {
     97     if (mTriggerRepository.isEmpty()) {
     98       stopSelfResult(startId);
     99       return;
    100     }
    101   }
    102 
    103   /** Returns the notification to display whenever the service is running. */
    104   @Override
    105   protected Notification createNotification() {
    106     Intent notificationIntent = new Intent(this, TriggerManager.class);
    107     Notification.Builder builder = new Notification.Builder(this);
    108     builder.setSmallIcon(R.drawable.sl4a_logo_48)
    109            .setTicker("SL4A Trigger Service started.")
    110            .setWhen(System.currentTimeMillis())
    111            .setContentTitle("SL4A Trigger Service")
    112            .setContentText("Tap to view triggers")
    113            .setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
    114     Notification notification = builder.build();
    115     notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
    116     return notification;
    117   }
    118 
    119   private class TriggerEventObserver implements EventObserver {
    120     private final Trigger mTrigger;
    121 
    122     public TriggerEventObserver(Trigger trigger) {
    123       mTrigger = trigger;
    124     }
    125 
    126     @Override
    127     public void onEventReceived(Event event) {
    128       mTrigger.handleEvent(event, TriggerService.this);
    129     }
    130   }
    131 
    132   private class RepositoryObserver implements TriggerRepositoryObserver {
    133     int mTriggerCount = 0;
    134 
    135     @Override
    136     public void onPut(Trigger trigger) {
    137       mTriggerCount++;
    138       mEventFacade.addNamedEventObserver(trigger.getEventName(), new TriggerEventObserver(trigger));
    139     }
    140 
    141     @Override
    142     public void onRemove(Trigger trigger) {
    143       Preconditions.checkArgument(mTriggerCount > 0);
    144       // TODO(damonkohler): Tear down EventObserver associated with trigger.
    145       if (--mTriggerCount == 0) {
    146         // TODO(damonkohler): Use stopSelfResult() which would require tracking startId.
    147         stopSelf();
    148       }
    149     }
    150   }
    151 
    152   private void installAlarm() {
    153     AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    154     alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + PING_MILLIS,
    155         PING_MILLIS, IntentBuilders.buildTriggerServicePendingIntent(this));
    156   }
    157 
    158   private void uninstallAlarm() {
    159     AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    160     alarmManager.cancel(IntentBuilders.buildTriggerServicePendingIntent(this));
    161   }
    162 
    163   @Override
    164   public void onDestroy() {
    165     super.onDestroy();
    166     uninstallAlarm();
    167   }
    168 }
    169