blob: 80245332d0906a70aaaf04502d28221294e9cf69 [file] [log] [blame]
/*
* Copyright (C) 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.googlecode.android_scripting.activity;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import com.google.common.base.Preconditions;
import com.googlecode.android_scripting.BaseApplication;
import com.googlecode.android_scripting.ForegroundService;
import com.googlecode.android_scripting.IntentBuilders;
import com.googlecode.android_scripting.NotificationIdFactory;
import com.googlecode.android_scripting.R;
import com.googlecode.android_scripting.event.Event;
import com.googlecode.android_scripting.event.EventObserver;
import com.googlecode.android_scripting.facade.EventFacade;
import com.googlecode.android_scripting.facade.FacadeConfiguration;
import com.googlecode.android_scripting.facade.FacadeManager;
import com.googlecode.android_scripting.trigger.EventGenerationControllingObserver;
import com.googlecode.android_scripting.trigger.Trigger;
import com.googlecode.android_scripting.trigger.TriggerRepository;
import com.googlecode.android_scripting.trigger.TriggerRepository.TriggerRepositoryObserver;
/**
* The trigger service takes care of installing triggers serialized to the preference storage.
*
* <p>
* The service also installs an alarm that keeps it running, unless the user force-quits the
* service.
*
* <p>
* When no triggers are installed the service shuts down silently as to not consume resources
* unnecessarily.
*
* @author Felix Arends (felix.arends@gmail.com)
* @author Damon Kohler (damonkohler@gmail.com)
*/
public class TriggerService extends ForegroundService {
private static final int NOTIFICATION_ID = NotificationIdFactory.create();
private static final long PING_MILLIS = 10 * 1000 * 60;
private final IBinder mBinder;
private TriggerRepository mTriggerRepository;
private FacadeManager mFacadeManager;
private EventFacade mEventFacade;
public class LocalBinder extends Binder {
public TriggerService getService() {
return TriggerService.this;
}
}
public TriggerService() {
super(NOTIFICATION_ID);
mBinder = new LocalBinder();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mFacadeManager =
new FacadeManager(FacadeConfiguration.getSdkLevel(), this, null,
FacadeConfiguration.getFacadeClasses());
mEventFacade = mFacadeManager.getReceiver(EventFacade.class);
mTriggerRepository = ((BaseApplication) getApplication()).getTriggerRepository();
mTriggerRepository.bootstrapObserver(new RepositoryObserver());
mTriggerRepository.bootstrapObserver(new EventGenerationControllingObserver(mFacadeManager));
installAlarm();
}
@Override
public void onStart(Intent intent, int startId) {
if (mTriggerRepository.isEmpty()) {
stopSelfResult(startId);
return;
}
}
/** Returns the notification to display whenever the service is running. */
@Override
protected Notification createNotification() {
Intent notificationIntent = new Intent(this, TriggerManager.class);
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.sl4a_logo_48)
.setTicker("SL4A Trigger Service started.")
.setWhen(System.currentTimeMillis())
.setContentTitle("SL4A Trigger Service")
.setContentText("Tap to view triggers")
.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
Notification notification = builder.build();
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
return notification;
}
private class TriggerEventObserver implements EventObserver {
private final Trigger mTrigger;
public TriggerEventObserver(Trigger trigger) {
mTrigger = trigger;
}
@Override
public void onEventReceived(Event event) {
mTrigger.handleEvent(event, TriggerService.this);
}
}
private class RepositoryObserver implements TriggerRepositoryObserver {
int mTriggerCount = 0;
@Override
public void onPut(Trigger trigger) {
mTriggerCount++;
mEventFacade.addNamedEventObserver(trigger.getEventName(), new TriggerEventObserver(trigger));
}
@Override
public void onRemove(Trigger trigger) {
Preconditions.checkArgument(mTriggerCount > 0);
// TODO(damonkohler): Tear down EventObserver associated with trigger.
if (--mTriggerCount == 0) {
// TODO(damonkohler): Use stopSelfResult() which would require tracking startId.
stopSelf();
}
}
}
private void installAlarm() {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + PING_MILLIS,
PING_MILLIS, IntentBuilders.buildTriggerServicePendingIntent(this));
}
private void uninstallAlarm() {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(IntentBuilders.buildTriggerServicePendingIntent(this));
}
@Override
public void onDestroy() {
super.onDestroy();
uninstallAlarm();
}
}