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 17package com.googlecode.android_scripting.activity; 18 19import android.app.AlarmManager; 20import android.app.Notification; 21import android.app.PendingIntent; 22import android.content.Context; 23import android.content.Intent; 24import android.os.Binder; 25import android.os.IBinder; 26 27import com.google.common.base.Preconditions; 28import com.googlecode.android_scripting.BaseApplication; 29import com.googlecode.android_scripting.ForegroundService; 30import com.googlecode.android_scripting.IntentBuilders; 31import com.googlecode.android_scripting.NotificationIdFactory; 32import com.googlecode.android_scripting.R; 33import com.googlecode.android_scripting.event.Event; 34import com.googlecode.android_scripting.event.EventObserver; 35import com.googlecode.android_scripting.facade.EventFacade; 36import com.googlecode.android_scripting.facade.FacadeConfiguration; 37import com.googlecode.android_scripting.facade.FacadeManager; 38import com.googlecode.android_scripting.trigger.EventGenerationControllingObserver; 39import com.googlecode.android_scripting.trigger.Trigger; 40import com.googlecode.android_scripting.trigger.TriggerRepository; 41import 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 */ 55public 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