Do not connect to server when user/pass is missing
[fonbot.git] / src / ro / ieval / fonbot / FonBotMainService.java
1 package ro.ieval.fonbot;
2
3 import static ro.ieval.fonbot.R.string.*;
4 import static ro.ieval.fonbot.Utils.toNonNull;
5
6 import java.net.SocketTimeoutException;
7 import java.util.Collections;
8 import java.util.EnumSet;
9 import java.util.Set;
10
11 import javax.net.ssl.SSLException;
12
13 import org.eclipse.jdt.annotation.Nullable;
14
15 import ro.ieval.fonbot.Address.Protocol;
16 import ro.ieval.fonbot.Utils.OngoingEvent;
17
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;
30
31 /*
32 * Copyright © 2013 Marius Gavrilescu
33 *
34 * This file is part of FonBot.
35 *
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.
40 *
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.
45 *
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/>.
48 */
49
50 /**
51 * Main service.
52 *
53 * @author Marius Gavrilescu
54 */
55 public final class FonBotMainService extends Service {
56 /**
57 * Binder for the service. It lets clients get a reference to the service.
58 *
59 * @author Marius Gavrilescu <marius@ieval.ro>
60 */
61 public final class Binder extends android.os.Binder{
62 /**
63 * Get a reference to the {@link FonBotMainService}
64 * @return a reference to the {@link FonBotMainService}
65 */
66 public FonBotMainService getService(){
67 return FonBotMainService.this;
68 }
69 }
70
71 /**
72 * Runnable that continously long polls the server for commands
73 *
74 * @author Marius Gavrilescu <marius@ieval.ro>
75 */
76 private final class LongPollRunnable implements Runnable{
77 public void run(){
78 final ConnectivityManager man=(ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
79 final HttpCallExecutableRunnable runnable=new HttpCallExecutableRunnable("/get", null, FonBotMainService.this, new PollResultCallback(FonBotMainService.this), false);
80
81 Log.d("LongPollRunnable", "Long polling started");
82 while(man.getActiveNetworkInfo() != null && man.getActiveNetworkInfo().isConnected())
83 try {
84 runnable.doRun();
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){
90 ex.printStackTrace();
91 break;
92 }
93 Log.d("LongPollRunnable", "Long polling stopped");
94 }
95 }
96
97 /**
98 * Broadcast action: add an ongoing event
99 */
100 public static final String ACTION_PUT_ONGOING="ro.ieval.fonbot.FonBotMainService.ACTION_PUT_ONGOING";
101 /**
102 * Broadcast action: remove an ongoing event
103 */
104 public static final String ACTION_DELETE_ONGOING="ro.ieval.fonbot.FonBotMainService.ACTION_DELETE_ONGOING";
105
106 /**
107 * Broadcast action: process a command received via SMS
108 */
109 public static final String ACTION_PROCESS_COMMAND="ro.ieval.fonbot.FonBotMainService.ACTION_PROCESS_COMMAND";
110
111 /**
112 * Extra: command line
113 */
114 public static final String EXTRA_COMMAND_LINE="ro.ieval.fonbot.FonBotMainService.EXTRA_COMMAND_LINE";
115
116 /**
117 * Extra: SMS originating address
118 */
119 public static final String EXTRA_SMS_ORIGIN_ADDRESS="ro.ieval.fonbot.FonBotMainService.EXTRA_SMS_ORIGIN_ADDRESS";
120
121 /**
122 * Extra: ongoing event id
123 *
124 * @see OngoingEvent
125 * @see #ACTION_DELETE_ONGOING
126 * @see #ACTION_PUT_ONGOING
127 */
128 public static final String EXTRA_ONGOING_ID="ro.ieval.fonbot.FonBotMainService.EXTRA_ONGOING_ID";
129 /**
130 * Broadcast sent when the ongoing event list is updated.
131 */
132 public static final String ACTION_ONGOING_UPDATE="ro.ieval.fonbot.FonBotMainService.ACTION_ONGOING_UPDATE";
133 /**
134 * IntentFilter for events caught by the {@link DynamicEventReceiver}
135 */
136 private static final IntentFilter DYNAMIC_BROADCAST_FILTER=new IntentFilter();
137
138 static{
139 DYNAMIC_BROADCAST_FILTER.addAction(Intent.ACTION_BATTERY_CHANGED);
140 DYNAMIC_BROADCAST_FILTER.addAction(Intent.ACTION_HEADSET_PLUG);
141 }
142
143 /**
144 * The one instance of {@link DynamicEventReceiver}
145 */
146 private final DynamicEventReceiver receiver=new DynamicEventReceiver();
147 /**
148 * Set of ongoing events.
149 */
150 private final Set<OngoingEvent> ongoing=EnumSet.noneOf(OngoingEvent.class);
151
152 /** true if running in foreground, false otherwise */
153 private boolean isForeground = false;
154
155 /** Thread that runs a {@link LongPollRunnable} */
156 private Thread longPollThread;
157
158 /**
159 * Get the set of ongoing events.
160 *
161 * @return a set of ongoing events
162 */
163 public Set<OngoingEvent> getOngoingEvents(){
164 return toNonNull(Collections.unmodifiableSet(ongoing));
165 }
166
167 @Override
168 public @Nullable IBinder onBind(final @Nullable Intent intent) {
169 return new Binder();
170 }
171
172 @Override
173 public void onCreate() {
174 super.onCreate();
175 registerReceiver(receiver, DYNAMIC_BROADCAST_FILTER);
176 }
177
178 @Override
179 public void onDestroy() {
180 super.onDestroy();
181 unregisterReceiver(receiver);
182 }
183
184 @Override
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;
192 }
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;
197 }
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);
205
206 Utils.processCommand(this, toNonNull(words[0]), args, toNonNull(address));
207 }
208
209 final String user=PreferenceManager.getDefaultSharedPreferences(this).getString("username", null);
210 final String password=PreferenceManager.getDefaultSharedPreferences(this).getString("password", null);
211 final boolean has_user_pass = user != null && password != null && user.length() > 0 && password.length() > 0;
212 if((longPollThread == null || !longPollThread.isAlive()) && has_user_pass){
213 longPollThread = new Thread(new LongPollRunnable());
214 longPollThread.start();
215 }
216
217 ExecutableRunnable.retryTasks();
218
219 final boolean runForeground=PreferenceManager.getDefaultSharedPreferences(this).getBoolean("foreground", false);
220 if(!runForeground)
221 stopForeground(true);
222
223 final NotificationManager man=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
224 final boolean shouldNotify=runForeground||!ongoing.isEmpty();
225 if(shouldNotify && (updateNotification || runForeground != isForeground)){
226 final Intent mainIntent=new Intent(this, FonBotMainActivity.class);
227 mainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
228 final NotificationCompat.Builder builder=new NotificationCompat.Builder(this).
229 setContentText(getString(foreground_notification_text)).
230 setContentTitle(getString(foreground_notification_title)).
231 setSmallIcon(android.R.drawable.stat_notify_sync_noanim).
232 setPriority(NotificationCompat.PRIORITY_MIN).
233 setContentIntent(PendingIntent.getActivity(this, 0, mainIntent, 0)).
234 setOngoing(true);
235
236 if(showOngoing && !ongoing.isEmpty()) {
237 final NotificationCompat.InboxStyle inboxBuilder=new NotificationCompat.InboxStyle(builder);
238
239 for(final OngoingEvent event : ongoing)
240 inboxBuilder.addLine(getString(event.resource));
241
242 }
243 final Notification notification=builder.build();
244
245 if(runForeground)
246 startForeground(1337, notification);
247 else
248 man.notify(1337, notification);
249 }
250
251 if(!shouldNotify)
252 man.cancel(1337);
253 isForeground=runForeground;
254 return START_STICKY;
255 }
256 }
This page took 0.026917 seconds and 4 git commands to generate.