1 package ro
.ieval
.fonbot
;
3 import static ro
.ieval
.fonbot
.R
.string
.*;
4 import static ro
.ieval
.fonbot
.Utils
.toNonNull
;
6 import java
.net
.SocketTimeoutException
;
7 import java
.util
.Collections
;
8 import java
.util
.EnumSet
;
11 import javax
.net
.ssl
.SSLException
;
13 import org
.eclipse
.jdt
.annotation
.Nullable
;
15 import ro
.ieval
.fonbot
.Address
.Protocol
;
16 import ro
.ieval
.fonbot
.Utils
.OngoingEvent
;
18 import android
.app
.Notification
;
19 import android
.app
.NotificationManager
;
20 import android
.app
.PendingIntent
;
21 import android
.app
.Service
;
22 import android
.content
.Intent
;
23 import android
.content
.IntentFilter
;
24 import android
.net
.ConnectivityManager
;
25 import android
.os
.IBinder
;
26 import android
.preference
.PreferenceManager
;
27 import android
.support
.v4
.app
.NotificationCompat
;
28 import android
.support
.v4
.content
.LocalBroadcastManager
;
29 import android
.util
.Log
;
32 * Copyright © 2013 Marius Gavrilescu
34 * This file is part of FonBot.
36 * FonBot is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or
39 * (at your option) any later version.
41 * FonBot is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with FonBot. If not, see <http://www.gnu.org/licenses/>.
53 * @author Marius Gavrilescu
55 public final class FonBotMainService
extends Service
{
57 * Binder for the service. It lets clients get a reference to the service.
59 * @author Marius Gavrilescu <marius@ieval.ro>
61 public final class Binder
extends android
.os
.Binder
{
63 * Get a reference to the {@link FonBotMainService}
64 * @return a reference to the {@link FonBotMainService}
66 public FonBotMainService
getService(){
67 return FonBotMainService
.this;
72 * Runnable that continously long polls the server for commands
74 * @author Marius Gavrilescu <marius@ieval.ro>
76 private final class LongPollRunnable
implements Runnable
{
78 final ConnectivityManager man
=(ConnectivityManager
) getSystemService(CONNECTIVITY_SERVICE
);
79 final HttpCallExecutableRunnable runnable
=new HttpCallExecutableRunnable("/get", null, FonBotMainService
.this, new PollResultCallback(FonBotMainService
.this), false);
81 Log
.d("LongPollRunnable", "Long polling started");
82 while(man
.getActiveNetworkInfo() != null && man
.getActiveNetworkInfo().isConnected())
85 } catch (final SocketTimeoutException e
){
86 Log
.d("LongPollRunnable", "Socket timeout, refreshing connection");
87 } catch (final SSLException e
){
88 Log
.d("LongPollRunnable", "Got SSLException, refreshing connection");
89 } catch (final Exception ex
){
93 Log
.d("LongPollRunnable", "Long polling stopped");
98 * Broadcast action: add an ongoing event
100 public static final String ACTION_PUT_ONGOING
="ro.ieval.fonbot.FonBotMainService.ACTION_PUT_ONGOING";
102 * Broadcast action: remove an ongoing event
104 public static final String ACTION_DELETE_ONGOING
="ro.ieval.fonbot.FonBotMainService.ACTION_DELETE_ONGOING";
107 * Broadcast action: process a command received via SMS
109 public static final String ACTION_PROCESS_COMMAND
="ro.ieval.fonbot.FonBotMainService.ACTION_PROCESS_COMMAND";
112 * Extra: command line
114 public static final String EXTRA_COMMAND_LINE
="ro.ieval.fonbot.FonBotMainService.EXTRA_COMMAND_LINE";
117 * Extra: SMS originating address
119 public static final String EXTRA_SMS_ORIGIN_ADDRESS
="ro.ieval.fonbot.FonBotMainService.EXTRA_SMS_ORIGIN_ADDRESS";
122 * Extra: ongoing event id
125 * @see #ACTION_DELETE_ONGOING
126 * @see #ACTION_PUT_ONGOING
128 public static final String EXTRA_ONGOING_ID
="ro.ieval.fonbot.FonBotMainService.EXTRA_ONGOING_ID";
130 * Broadcast sent when the ongoing event list is updated.
132 public static final String ACTION_ONGOING_UPDATE
="ro.ieval.fonbot.FonBotMainService.ACTION_ONGOING_UPDATE";
134 * IntentFilter for events caught by the {@link DynamicEventReceiver}
136 private static final IntentFilter DYNAMIC_BROADCAST_FILTER
=new IntentFilter();
139 DYNAMIC_BROADCAST_FILTER
.addAction(Intent
.ACTION_BATTERY_CHANGED
);
140 DYNAMIC_BROADCAST_FILTER
.addAction(Intent
.ACTION_HEADSET_PLUG
);
144 * The one instance of {@link DynamicEventReceiver}
146 private final DynamicEventReceiver receiver
=new DynamicEventReceiver();
148 * Set of ongoing events.
150 private final Set
<OngoingEvent
> ongoing
=EnumSet
.noneOf(OngoingEvent
.class);
152 /** true if running in foreground, false otherwise */
153 private boolean isForeground
= false;
155 /** Thread that runs a {@link LongPollRunnable} */
156 private Thread longPollThread
;
159 * Get the set of ongoing events.
161 * @return a set of ongoing events
163 public Set
<OngoingEvent
> getOngoingEvents(){
164 return toNonNull(Collections
.unmodifiableSet(ongoing
));
168 public @Nullable IBinder
onBind(final @Nullable Intent intent
) {
173 public void onCreate() {
175 registerReceiver(receiver
, DYNAMIC_BROADCAST_FILTER
);
179 public void onDestroy() {
181 unregisterReceiver(receiver
);
185 public int onStartCommand(final @Nullable Intent intent
, final int flags
, final int startId
) {
186 final boolean showOngoing
=PreferenceManager
.getDefaultSharedPreferences(this).getBoolean("ongoing", false);
187 boolean updateNotification
= false;
188 if(intent
!=null && intent
.getAction()==ACTION_PUT_ONGOING
&& intent
.hasExtra(EXTRA_ONGOING_ID
) && showOngoing
){
189 ongoing
.add(OngoingEvent
.values()[intent
.getIntExtra(EXTRA_ONGOING_ID
, 0)]);
190 LocalBroadcastManager
.getInstance(this).sendBroadcast(new Intent(ACTION_ONGOING_UPDATE
));
191 updateNotification
=true;
193 if(intent
!=null && intent
.getAction()==ACTION_DELETE_ONGOING
&& intent
.hasExtra(EXTRA_ONGOING_ID
) && showOngoing
){
194 ongoing
.remove(OngoingEvent
.values()[intent
.getIntExtra(EXTRA_ONGOING_ID
, 0)]);
195 LocalBroadcastManager
.getInstance(this).sendBroadcast(new Intent(ACTION_ONGOING_UPDATE
));
196 updateNotification
=true;
198 if(intent
!=null && intent
.getAction()==ACTION_PROCESS_COMMAND
) {
199 final String cmdline
= intent
.getStringExtra(EXTRA_COMMAND_LINE
);
200 final String origin
= intent
.getStringExtra(EXTRA_SMS_ORIGIN_ADDRESS
);
201 final Address address
= new Address(toNonNull(Protocol
.SMS
), origin
);//NOPMD variable depends on originAddress
202 final String
[] words
=Utils
.shellwords(toNonNull(cmdline
));
203 final String
[] args
=new String
[words
.length
-1];//NOPMD variable size depends on words.length
204 System
.arraycopy(words
, 1, args
, 0, args
.length
);
206 Utils
.processCommand(this, toNonNull(words
[0]), args
, toNonNull(address
));
209 if(longPollThread
== null || !longPollThread
.isAlive()){
210 longPollThread
= new Thread(new LongPollRunnable());
211 longPollThread
.start();
214 ExecutableRunnable
.retryTasks();
216 final boolean runForeground
=PreferenceManager
.getDefaultSharedPreferences(this).getBoolean("foreground", false);
218 stopForeground(true);
220 final NotificationManager man
=(NotificationManager
) getSystemService(NOTIFICATION_SERVICE
);
221 final boolean shouldNotify
=runForeground
||!ongoing
.isEmpty();
222 if(shouldNotify
&& (updateNotification
|| runForeground
!= isForeground
)){
223 final Intent mainIntent
=new Intent(this, FonBotMainActivity
.class);
224 mainIntent
.addFlags(Intent
.FLAG_ACTIVITY_NEW_TASK
);
225 final NotificationCompat
.Builder builder
=new NotificationCompat
.Builder(this).
226 setContentText(getString(foreground_notification_text
)).
227 setContentTitle(getString(foreground_notification_title
)).
228 setSmallIcon(android
.R
.drawable
.stat_notify_sync_noanim
).
229 setPriority(NotificationCompat
.PRIORITY_MIN
).
230 setContentIntent(PendingIntent
.getActivity(this, 0, mainIntent
, 0)).
233 if(showOngoing
&& !ongoing
.isEmpty()) {
234 final NotificationCompat
.InboxStyle inboxBuilder
=new NotificationCompat
.InboxStyle(builder
);
236 for(final OngoingEvent event
: ongoing
)
237 inboxBuilder
.addLine(getString(event
.resource
));
240 final Notification notification
=builder
.build();
243 startForeground(1337, notification
);
245 man
.notify(1337, notification
);
250 isForeground
=runForeground
;