Execute commands received via SMS on the main service
[fonbot.git] / src / ro / ieval / fonbot / FonBotMainService.java
CommitLineData
8dfb76c9
MG
1package ro.ieval.fonbot;
2
3import static ro.ieval.fonbot.R.string.*;
4import static ro.ieval.fonbot.Utils.toNonNull;
5
82a2d0c2 6import java.net.SocketTimeoutException;
8dfb76c9 7import java.util.Collections;
5e3858a3 8import java.util.EnumSet;
8dfb76c9
MG
9import java.util.Set;
10
11import org.eclipse.jdt.annotation.Nullable;
12
e0c7f061 13import ro.ieval.fonbot.Address.Protocol;
8dfb76c9
MG
14import ro.ieval.fonbot.Utils.OngoingEvent;
15
16import android.app.Notification;
17import android.app.NotificationManager;
18import android.app.PendingIntent;
19import android.app.Service;
20import android.content.Intent;
21import android.content.IntentFilter;
ca40a5cc 22import android.net.ConnectivityManager;
8dfb76c9
MG
23import android.os.IBinder;
24import android.preference.PreferenceManager;
25import android.support.v4.app.NotificationCompat;
26import android.support.v4.content.LocalBroadcastManager;
ca40a5cc 27import android.util.Log;
8dfb76c9
MG
28
29/*
30 * Copyright © 2013 Marius Gavrilescu
31 *
32 * This file is part of FonBot.
33 *
34 * FonBot is free software: you can redistribute it and/or modify
35 * it under the terms of the GNU General Public License as published by
36 * the Free Software Foundation, either version 3 of the License, or
37 * (at your option) any later version.
38 *
39 * FonBot is distributed in the hope that it will be useful,
40 * but WITHOUT ANY WARRANTY; without even the implied warranty of
41 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 * GNU General Public License for more details.
43 *
44 * You should have received a copy of the GNU General Public License
45 * along with FonBot. If not, see <http://www.gnu.org/licenses/>.
46 */
47
48/**
49 * Main service.
50 *
51 * @author Marius Gavrilescu
52 */
53public final class FonBotMainService extends Service {
54 /**
55 * Binder for the service. It lets clients get a reference to the service.
56 *
57 * @author Marius Gavrilescu <marius@ieval.ro>
58 */
59 public final class Binder extends android.os.Binder{
60 /**
61 * Get a reference to the {@link FonBotMainService}
62 * @return a reference to the {@link FonBotMainService}
63 */
64 public FonBotMainService getService(){
65 return FonBotMainService.this;
66 }
67 }
68
ca40a5cc
MG
69 /**
70 * Runnable that continously long polls the server for commands
71 *
72 * @author Marius Gavrilescu <marius@ieval.ro>
73 */
74 private final class LongPollRunnable implements Runnable{
75 public void run(){
76 final ConnectivityManager man=(ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
77 final HttpCallExecutableRunnable runnable=new HttpCallExecutableRunnable("/get", null, FonBotMainService.this, new PollResultCallback(FonBotMainService.this), false);
78
79 Log.d("LongPollRunnable", "Long polling started");
80 while(man.getActiveNetworkInfo() != null && man.getActiveNetworkInfo().isConnected())
81 try {
82a2d0c2
MG
82 runnable.doRun();
83 } catch (final SocketTimeoutException e){
84 Log.d("LongPollRunnable", "Socket timeout, refreshing connection");
ca40a5cc
MG
85 } catch (final Exception ex){
86 ex.printStackTrace();
c7b1fdf5 87 break;
ca40a5cc
MG
88 }
89 Log.d("LongPollRunnable", "Long polling stopped");
90 }
91 }
92
8dfb76c9
MG
93 /**
94 * Broadcast action: add an ongoing event
95 */
96 public static final String ACTION_PUT_ONGOING="ro.ieval.fonbot.FonBotMainService.ACTION_PUT_ONGOING";
97 /**
98 * Broadcast action: remove an ongoing event
99 */
100 public static final String ACTION_DELETE_ONGOING="ro.ieval.fonbot.FonBotMainService.ACTION_DELETE_ONGOING";
1b6a0480 101
e0c7f061
MG
102 /**
103 * Broadcast action: process a command received via SMS
104 */
105 public static final String ACTION_PROCESS_COMMAND="ro.ieval.fonbot.FonBotMainService.ACTION_PROCESS_COMMAND";
106
107 /**
108 * Extra: command line
109 */
110 public static final String EXTRA_COMMAND_LINE="ro.ieval.fonbot.FonBotMainService.EXTRA_COMMAND_LINE";
111
112 /**
113 * Extra: SMS originating address
114 */
115 public static final String EXTRA_SMS_ORIGIN_ADDRESS="ro.ieval.fonbot.FonBotMainService.EXTRA_SMS_ORIGIN_ADDRESS";
116
8dfb76c9
MG
117 /**
118 * Extra: ongoing event id
119 *
120 * @see OngoingEvent
121 * @see #ACTION_DELETE_ONGOING
122 * @see #ACTION_PUT_ONGOING
123 */
124 public static final String EXTRA_ONGOING_ID="ro.ieval.fonbot.FonBotMainService.EXTRA_ONGOING_ID";
125 /**
126 * Broadcast sent when the ongoing event list is updated.
127 */
128 public static final String ACTION_ONGOING_UPDATE="ro.ieval.fonbot.FonBotMainService.ACTION_ONGOING_UPDATE";
129 /**
130 * IntentFilter for events caught by the {@link DynamicEventReceiver}
131 */
132 private static final IntentFilter DYNAMIC_BROADCAST_FILTER=new IntentFilter();
133
134 static{
8dfb76c9
MG
135 DYNAMIC_BROADCAST_FILTER.addAction(Intent.ACTION_BATTERY_CHANGED);
136 DYNAMIC_BROADCAST_FILTER.addAction(Intent.ACTION_HEADSET_PLUG);
137 }
138
139 /**
140 * The one instance of {@link DynamicEventReceiver}
141 */
142 private final DynamicEventReceiver receiver=new DynamicEventReceiver();
143 /**
144 * Set of ongoing events.
145 */
5e3858a3 146 private final Set<OngoingEvent> ongoing=EnumSet.noneOf(OngoingEvent.class);
8dfb76c9 147
c4351ec6
MG
148 /** true if running in foreground, false otherwise */
149 private boolean isForeground = false;
150
ca40a5cc
MG
151 /** Thread that runs a {@link LongPollRunnable} */
152 private Thread longPollThread;
153
8dfb76c9
MG
154 /**
155 * Get the set of ongoing events.
156 *
157 * @return a set of ongoing events
158 */
159 public Set<OngoingEvent> getOngoingEvents(){
160 return toNonNull(Collections.unmodifiableSet(ongoing));
161 }
162
163 @Override
164 public @Nullable IBinder onBind(final @Nullable Intent intent) {
165 return new Binder();
166 }
167
168 @Override
169 public void onCreate() {
170 super.onCreate();
171 registerReceiver(receiver, DYNAMIC_BROADCAST_FILTER);
172 }
173
174 @Override
175 public void onDestroy() {
176 super.onDestroy();
177 unregisterReceiver(receiver);
178 }
179
180 @Override
181 public int onStartCommand(final @Nullable Intent intent, final int flags, final int startId) {
079ea306 182 final boolean showOngoing=PreferenceManager.getDefaultSharedPreferences(this).getBoolean("ongoing", false);
c4351ec6 183 boolean updateNotification = false;
079ea306 184 if(intent!=null && intent.getAction()==ACTION_PUT_ONGOING && intent.hasExtra(EXTRA_ONGOING_ID) && showOngoing){
8dfb76c9
MG
185 ongoing.add(OngoingEvent.values()[intent.getIntExtra(EXTRA_ONGOING_ID, 0)]);
186 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_ONGOING_UPDATE));
c4351ec6 187 updateNotification=true;
8dfb76c9 188 }
079ea306 189 if(intent!=null && intent.getAction()==ACTION_DELETE_ONGOING && intent.hasExtra(EXTRA_ONGOING_ID) && showOngoing){
8dfb76c9
MG
190 ongoing.remove(OngoingEvent.values()[intent.getIntExtra(EXTRA_ONGOING_ID, 0)]);
191 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_ONGOING_UPDATE));
c4351ec6 192 updateNotification=true;
8dfb76c9 193 }
e0c7f061
MG
194 if(intent!=null && intent.getAction()==ACTION_PROCESS_COMMAND) {
195 final String cmdline = intent.getStringExtra(EXTRA_COMMAND_LINE);
196 final String origin = intent.getStringExtra(EXTRA_SMS_ORIGIN_ADDRESS);
197 final Address address= new Address(toNonNull(Protocol.SMS), origin);//NOPMD variable depends on originAddress
198 final String[] words=Utils.shellwords(toNonNull(cmdline));
199 final String[] args=new String[words.length-1];//NOPMD variable size depends on words.length
200 System.arraycopy(words, 1, args, 0, args.length);
201
202 Utils.processCommand(this, toNonNull(words[0]), args, toNonNull(address));
203 }
8dfb76c9 204
ca40a5cc
MG
205 if(longPollThread == null || !longPollThread.isAlive()){
206 longPollThread = new Thread(new LongPollRunnable());
207 longPollThread.start();
208 }
6b9507db 209
c4f6e512
MG
210 ExecutableRunnable.retryTasks();
211
8dfb76c9
MG
212 final boolean runForeground=PreferenceManager.getDefaultSharedPreferences(this).getBoolean("foreground", false);
213 if(!runForeground)
214 stopForeground(true);
215
c4351ec6
MG
216 final NotificationManager man=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
217 final boolean shouldNotify=runForeground||!ongoing.isEmpty();
218 if(shouldNotify && (updateNotification || runForeground != isForeground)){
8dfb76c9
MG
219 final Intent mainIntent=new Intent(this, FonBotMainActivity.class);
220 mainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
221 final NotificationCompat.Builder builder=new NotificationCompat.Builder(this).
222 setContentText(getString(foreground_notification_text)).
223 setContentTitle(getString(foreground_notification_title)).
224 setSmallIcon(android.R.drawable.stat_notify_sync_noanim).
225 setPriority(NotificationCompat.PRIORITY_MIN).
226 setContentIntent(PendingIntent.getActivity(this, 0, mainIntent, 0)).
227 setOngoing(true);
228
079ea306 229 if(showOngoing && !ongoing.isEmpty()) {
8dfb76c9
MG
230 final NotificationCompat.InboxStyle inboxBuilder=new NotificationCompat.InboxStyle(builder);
231
232 for(final OngoingEvent event : ongoing)
233 inboxBuilder.addLine(getString(event.resource));
234
8dfb76c9 235 }
c4351ec6 236 final Notification notification=builder.build();
8dfb76c9
MG
237
238 if(runForeground)
c4351ec6
MG
239 startForeground(1337, notification);
240 else
8dfb76c9 241 man.notify(1337, notification);
8dfb76c9
MG
242 }
243
c4351ec6
MG
244 if(!shouldNotify)
245 man.cancel(1337);
246 isForeground=runForeground;
8dfb76c9
MG
247 return START_STICKY;
248 }
249}
This page took 0.029862 seconds and 4 git commands to generate.