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