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