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@gmail.com) 55 * @author Damon Kohler (damonkohler@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 { getService()67 public TriggerService getService() { 68 return TriggerService.this; 69 } 70 } 71 TriggerService()72 public TriggerService() { 73 super(NOTIFICATION_ID); 74 mBinder = new LocalBinder(); 75 } 76 77 @Override onBind(Intent intent)78 public IBinder onBind(Intent intent) { 79 return mBinder; 80 } 81 82 @Override onCreate()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 onStart(Intent intent, int startId)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 createNotification()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 TriggerEventObserver(Trigger trigger)124 public TriggerEventObserver(Trigger trigger) { 125 mTrigger = trigger; 126 } 127 128 @Override onEventReceived(Event event)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 onPut(Trigger trigger)138 public void onPut(Trigger trigger) { 139 mTriggerCount++; 140 mEventFacade.addNamedEventObserver(trigger.getEventName(), new TriggerEventObserver(trigger)); 141 } 142 143 @Override onRemove(Trigger trigger)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 installAlarm()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 uninstallAlarm()160 private void uninstallAlarm() { 161 AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 162 alarmManager.cancel(IntentBuilders.buildTriggerServicePendingIntent(this)); 163 } 164 165 @Override onDestroy()166 public void onDestroy() { 167 super.onDestroy(); 168 uninstallAlarm(); 169 } 170 } 171