9b4be25519c79025dc777149afd2aa9c046358e7
[public/netxms.git] / android / src / console / src / org / netxms / ui / android / service / ClientConnectorService.java
1 /**
2 *
3 */
4 package org.netxms.ui.android.service;
5
6 import java.text.DateFormat;
7 import java.util.ArrayList;
8 import java.util.Calendar;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Set;
12
13 import org.netxms.api.client.SessionListener;
14 import org.netxms.api.client.SessionNotification;
15 import org.netxms.base.Logger;
16 import org.netxms.client.NXCNotification;
17 import org.netxms.client.NXCSession;
18 import org.netxms.client.datacollection.DciValue;
19 import org.netxms.client.events.Alarm;
20 import org.netxms.client.objects.GenericObject;
21 import org.netxms.client.objecttools.ObjectTool;
22 import org.netxms.ui.android.NXApplication;
23 import org.netxms.ui.android.R;
24 import org.netxms.ui.android.helpers.SafeParser;
25 import org.netxms.ui.android.main.activities.AlarmBrowser;
26 import org.netxms.ui.android.main.activities.DashboardBrowser;
27 import org.netxms.ui.android.main.activities.GraphBrowser;
28 import org.netxms.ui.android.main.activities.HomeScreen;
29 import org.netxms.ui.android.main.activities.NodeBrowser;
30 import org.netxms.ui.android.receivers.AlarmIntentReceiver;
31 import org.netxms.ui.android.service.helpers.AndroidLoggingFacility;
32 import org.netxms.ui.android.service.tasks.ConnectTask;
33 import org.netxms.ui.android.service.tasks.ExecActionTask;
34
35 import android.app.AlarmManager;
36 import android.app.Notification;
37 import android.app.NotificationManager;
38 import android.app.PendingIntent;
39 import android.app.Service;
40 import android.content.BroadcastReceiver;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.content.SharedPreferences;
45 import android.content.SharedPreferences.Editor;
46 import android.content.res.Resources;
47 import android.media.Ringtone;
48 import android.media.RingtoneManager;
49 import android.net.Uri;
50 import android.os.Binder;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.preference.PreferenceManager;
54 import android.support.v4.app.NotificationCompat;
55 import android.support.v4.content.Loader;
56 import android.util.Log;
57 import android.widget.Toast;
58
59 /**
60 * Background communication service for NetXMS client.
61 *
62 * @author Victor Kirhenshtein
63 * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
64 *
65 */
66
67 public class ClientConnectorService extends Service implements SessionListener
68 {
69 public enum ConnectionStatus
70 {
71 CS_NOCONNECTION, CS_INPROGRESS, CS_ALREADYCONNECTED, CS_CONNECTED, CS_DISCONNECTED, CS_ERROR
72 };
73
74 public static final String ACTION_CONNECT = "org.netxms.ui.android.ACTION_CONNECT";
75 public static final String ACTION_FORCE_CONNECT = "org.netxms.ui.android.ACTION_FORCE_CONNECT";
76 public static final String ACTION_DISCONNECT = "org.netxms.ui.android.ACTION_DISCONNECT";
77 public static final String ACTION_FORCE_DISCONNECT = "org.netxms.ui.android.ACTION_FORCE_DISCONNECT";
78 public static final String ACTION_RESCHEDULE = "org.netxms.ui.android.ACTION_RESCHEDULE";
79 public static final String ACTION_CONFIGURE = "org.netxms.ui.android.ACTION_CONFIGURE";
80 private static final String TAG = "nxclient/ClientConnectorService";
81 private static final String LASTALARM_KEY = "LastALarmIdNotified";
82
83 private static final int NOTIFY_ALARM = 1;
84 private static final int NOTIFY_STATUS = 2;
85 private static final int NOTIFY_STATUS_ON_CONNECT = 1;
86 private static final int NOTIFY_STATUS_ON_DISCONNECT = 2;
87 private static final int NOTIFY_STATUS_ALWAYS = 3;
88 private static final int ONE_DAY_MINUTES = 24 * 60;
89 private static final int NETXMS_REQUEST_CODE = 123456;
90
91 private final String mutex = "MUTEX";
92 private final Binder binder = new ClientConnectorBinder();
93 private Handler uiThreadHandler;
94 private NotificationManager notificationManager;
95 private NXCSession session = null;
96 private ConnectionStatus connectionStatus = ConnectionStatus.CS_DISCONNECTED;
97 private String connectionStatusText = "";
98 private int connectionStatusColor = 0;
99 private Map<Long, Alarm> alarms = null;
100 private HomeScreen homeScreen = null;
101 private AlarmBrowser alarmBrowser = null;
102 private NodeBrowser nodeBrowser = null;
103 private GraphBrowser graphBrowser = null;
104 private Alarm unknownAlarm = null;
105 private long lastAlarmIdNotified;
106 private List<ObjectTool> objectTools = null;
107 private BroadcastReceiver receiver = null;
108 private DashboardBrowser dashboardBrowser;
109 private SharedPreferences sp;
110 private final List<Loader<Alarm[]>> alarmLoaders = new ArrayList<Loader<Alarm[]>>(0);
111 private final List<Loader<DciValue[]>> dciValueLoaders = new ArrayList<Loader<DciValue[]>>(0);
112 private final List<Loader<GenericObject>> genericObjectLoaders = new ArrayList<Loader<GenericObject>>(0);
113 private final List<Loader<Set<GenericObject>>> genericObjectChildrenLoaders = new ArrayList<Loader<Set<GenericObject>>>(0);
114 private String server = "";
115 private int port = 4701;
116 private String login = "";
117 private String password = "";
118 private boolean encrypt = false;
119 private boolean enabled = false;
120 private boolean notifyAlarm = false;
121 private int notificationType = 0;
122 private boolean notifyIcon = false;
123 private boolean notifyToast = false;
124 private int schedulerPostpone = 0;
125 private int schedulerDuration = 0;
126 private int schedulerInterval = 0;
127 private boolean schedulerDailyEnabled = false;
128 private int schedulerDailyOn = 0;
129 private int schedulerDailyOff = 0;
130
131 /**
132 * Class for clients to access. Because we know this service always runs in
133 * the same process as its clients, we don't need to deal with IPC.
134 */
135 public class ClientConnectorBinder extends Binder
136 {
137 public ClientConnectorService getService()
138 {
139 return ClientConnectorService.this;
140 }
141 }
142
143 /*
144 * (non-Javadoc)
145 *
146 * @see android.app.Service#onCreate()
147 */
148 @Override
149 public void onCreate()
150 {
151 super.onCreate();
152
153 Logger.setLoggingFacility(new AndroidLoggingFacility());
154 uiThreadHandler = new Handler(getMainLooper());
155 notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
156
157 showToast(getString(R.string.notify_started));
158
159 sp = PreferenceManager.getDefaultSharedPreferences(this);
160 lastAlarmIdNotified = sp.getInt(LASTALARM_KEY, 0);
161 configure();
162
163 receiver = new BroadcastReceiver()
164 {
165 @Override
166 public void onReceive(Context context, Intent intent)
167 {
168 Intent i = new Intent(context, ClientConnectorService.class);
169 i.setAction(ACTION_RESCHEDULE);
170 context.startService(i);
171 }
172 };
173 registerReceiver(receiver, new IntentFilter(Intent.ACTION_TIME_TICK));
174
175 if (NXApplication.isActivityVisible())
176 reconnect(false);
177 }
178
179 /*
180 * (non-Javadoc)
181 *
182 * @see android.app.Service#onStartCommand(android.content.Intent, int, int)
183 */
184 @Override
185 public int onStartCommand(Intent intent, int flags, int startId)
186 {
187 if ((intent != null) && (intent.getAction() != null))
188 if (intent.getAction().equals(ACTION_CONNECT))
189 reconnect(false);
190 else if (intent.getAction().equals(ACTION_FORCE_CONNECT))
191 reconnect(true);
192 else if (intent.getAction().equals(ACTION_DISCONNECT))
193 disconnect(false);
194 else if (intent.getAction().equals(ACTION_FORCE_DISCONNECT))
195 disconnect(true);
196 else if (intent.getAction().equals(ACTION_RESCHEDULE))
197 if (NXApplication.isActivityVisible())
198 reconnect(false);
199 else
200 disconnect(false);
201 else if (intent.getAction().equals(ACTION_CONFIGURE))
202 reconnect(configure());
203 return super.onStartCommand(intent, flags, startId);
204 }
205
206 /*
207 * (non-Javadoc)
208 *
209 * @see android.app.Service#onBind(android.content.Intent)
210 */
211 @Override
212 public IBinder onBind(Intent intent)
213 {
214 return binder;
215 }
216
217 /*
218 * (non-Javadoc)
219 *
220 * @see android.app.Service#onDestroy()
221 */
222 @Override
223 public void onDestroy()
224 {
225 super.onDestroy();
226 }
227
228 /**
229 * Shutdown background service
230 */
231 public void shutdown()
232 {
233 cancelSchedule();
234 clearNotifications();
235 savePreferences();
236 unregisterReceiver(receiver);
237 stopSelf();
238 }
239
240 /**
241 *
242 */
243 public void savePreferences()
244 {
245 SharedPreferences.Editor editor = sp.edit();
246 editor.putInt(LASTALARM_KEY, (int)lastAlarmIdNotified);
247 editor.commit();
248 }
249
250 /**
251 * Show alarm notification
252 *
253 * @param severity notification severity (used to determine icon and sound)
254 * @param text notification text
255 */
256 public void alarmNotification(int severity, String text)
257 {
258 if (notifyAlarm)
259 {
260 Intent notifyIntent = new Intent(getApplicationContext(), AlarmBrowser.class);
261 PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0, notifyIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
262 NotificationCompat.Builder nb = new NotificationCompat.Builder(getApplicationContext())
263 .setSmallIcon(getAlarmIcon(severity))
264 .setWhen(System.currentTimeMillis())
265 .setDefaults(Notification.DEFAULT_LIGHTS)
266 .setContentText(text)
267 .setContentTitle(getString(R.string.notification_title))
268 .setContentIntent(intent);
269 final String sound = GetAlarmSound(severity);
270 if ((sound != null) && (sound.length() > 0))
271 nb.setSound(Uri.parse(sound));
272 notificationManager.notify(NOTIFY_ALARM, nb.build());
273 }
274 else
275 {
276 Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), Uri.parse(GetAlarmSound(severity)));
277 if (r != null)
278 r.play();
279 }
280 }
281 /**
282 * Show status notification
283 *
284 * @param status connection status
285 * @param extra extra text to add at the end of the toast
286 */
287 public void statusNotification(ConnectionStatus status, String extra)
288 {
289 int icon = -1;
290 String text = "";
291 switch (status)
292 {
293 case CS_CONNECTED:
294 if (notificationType == NOTIFY_STATUS_ON_CONNECT || notificationType == NOTIFY_STATUS_ALWAYS)
295 {
296 icon = R.drawable.ic_stat_connected;
297 }
298 text = getString(R.string.notify_connected, extra);
299 break;
300 case CS_DISCONNECTED:
301 if (notificationType == NOTIFY_STATUS_ON_DISCONNECT || notificationType == NOTIFY_STATUS_ALWAYS)
302 {
303 icon = R.drawable.ic_stat_disconnected;
304 }
305 text = getString(R.string.notify_disconnected) + getNextConnectionRetry();
306 break;
307 case CS_ERROR:
308 if (notificationType == NOTIFY_STATUS_ON_DISCONNECT || notificationType == NOTIFY_STATUS_ALWAYS)
309 {
310 icon = R.drawable.ic_stat_disconnected;
311 }
312 text = getString(R.string.notify_connection_failed, extra);
313 break;
314 case CS_NOCONNECTION:
315 case CS_INPROGRESS:
316 case CS_ALREADYCONNECTED:
317 default:
318 return;
319 }
320 if (icon == -1)
321 hideNotification(NOTIFY_STATUS);
322 else
323 {
324 if (notifyToast)
325 showToast(text);
326 if (notifyIcon)
327 {
328 Intent notifyIntent = new Intent(getApplicationContext(), HomeScreen.class);
329 PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0, notifyIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
330 NotificationCompat.Builder nb = new NotificationCompat.Builder(getApplicationContext())
331 .setSmallIcon(icon)
332 .setWhen(System.currentTimeMillis())
333 .setAutoCancel(false)
334 .setOnlyAlertOnce(true)
335 .setOngoing(true)
336 .setContentText(text)
337 .setContentTitle(getString(R.string.notification_title))
338 .setContentIntent(intent);
339 notificationManager.notify(NOTIFY_STATUS, nb.build());
340 }
341 }
342 }
343
344 /**
345 * Hide notification
346 *
347 * @param id
348 * notification id
349 */
350 private void hideNotification(int id)
351 {
352 notificationManager.cancel(id);
353 }
354
355 /**
356 * Clear all notifications
357 */
358 public void clearNotifications()
359 {
360 notificationManager.cancelAll();
361 }
362
363 /**
364 * Configure service, notify callers if it is necessary to reconnect
365 * depending if a particular subset of parameters have been changed.
366 *
367 * @return true If it is necessary to force a reconnection
368 */
369 private boolean configure()
370 {
371 boolean needsToReconnect = enabled != sp.getBoolean("global.scheduler.enable", false) ||
372 server != sp.getString("connection.server", "") ||
373 port != SafeParser.parseInt(sp.getString("connection.port", "4701"), 4701) ||
374 login != sp.getString("connection.login", "") ||
375 password != sp.getString("connection.password", "") ||
376 encrypt != sp.getBoolean("connection.encrypt", false) ||
377 notifyAlarm != sp.getBoolean("global.notification.alarm", true) ||
378 notificationType != Integer.parseInt(sp.getString("global.notification.status", "0")) ||
379 notifyIcon != sp.getBoolean("global.notification.icon", false) ||
380 notifyToast != sp.getBoolean("global.notification.toast", true) ||
381 schedulerPostpone != Integer.parseInt(sp.getString("global.scheduler.postpone", "1")) ||
382 schedulerDuration != Integer.parseInt(sp.getString("global.scheduler.duration", "1")) ||
383 schedulerInterval != Integer.parseInt(sp.getString("global.scheduler.interval", "15")) ||
384 schedulerDailyEnabled != sp.getBoolean("global.scheduler.daily.enable", false) ||
385 schedulerDailyOn != getMinutes("global.scheduler.daily.on") ||
386 schedulerDailyOff != getMinutes("global.scheduler.daily.off");
387
388 enabled = sp.getBoolean("global.scheduler.enable", false);
389 server = sp.getString("connection.server", "");
390 port = SafeParser.parseInt(sp.getString("connection.port", "4701"), 4701);
391 login = sp.getString("connection.login", "");
392 password = sp.getString("connection.password", "");
393 encrypt = sp.getBoolean("connection.encrypt", false);
394 notifyAlarm = sp.getBoolean("global.notification.alarm", true);
395 notificationType = Integer.parseInt(sp.getString("global.notification.status", "0"));
396 notifyIcon = sp.getBoolean("global.notification.icon", false);
397 notifyToast = sp.getBoolean("global.notification.toast", true);
398 schedulerPostpone = Integer.parseInt(sp.getString("global.scheduler.postpone", "1"));
399 schedulerDuration = Integer.parseInt(sp.getString("global.scheduler.duration", "1"));
400 schedulerInterval = Integer.parseInt(sp.getString("global.scheduler.interval", "15"));
401 schedulerDailyEnabled = sp.getBoolean("global.scheduler.daily.enable", false);
402 schedulerDailyOn = getMinutes("global.scheduler.daily.on");
403 schedulerDailyOff = getMinutes("global.scheduler.daily.off");
404
405 return needsToReconnect;
406 }
407
408 /**
409 * Reconnect to server.
410 *
411 * @param forceReconnect if set to true forces disconnection before connecting
412 */
413 public void reconnect(boolean force)
414 {
415 if (force || (isScheduleExpired() || NXApplication.isActivityVisible()) &&
416 connectionStatus != ConnectionStatus.CS_CONNECTED &&
417 connectionStatus != ConnectionStatus.CS_ALREADYCONNECTED)
418 {
419 Log.i(TAG, "Reconnecting...");
420 synchronized (mutex)
421 {
422 if (connectionStatus != ConnectionStatus.CS_INPROGRESS)
423 {
424 setConnectionStatus(ConnectionStatus.CS_INPROGRESS, "");
425 statusNotification(ConnectionStatus.CS_INPROGRESS, "");
426 new ConnectTask(this).execute(server, port, login, password, encrypt, force);
427 }
428 }
429 }
430 }
431
432 /**
433 * Disconnect from server. Only when scheduler is enabled and connected.
434 *
435 */
436 public void disconnect(boolean force)
437 {
438 if (force || enabled && !NXApplication.isActivityVisible() &&
439 (connectionStatus == ConnectionStatus.CS_CONNECTED ||
440 connectionStatus == ConnectionStatus.CS_ALREADYCONNECTED))
441 {
442 Log.i(TAG, "Disconnecting...");
443 nullifySession();
444 setConnectionStatus(ConnectionStatus.CS_DISCONNECTED, "");
445 statusNotification(ConnectionStatus.CS_DISCONNECTED, "");
446 }
447 }
448
449 /**
450 * Called by connect task after successful connection
451 *
452 * @param session
453 * new session object
454 */
455 public void onConnect(NXCSession session, Map<Long, Alarm> alarms)
456 {
457 synchronized (mutex)
458 {
459 if (session != null)
460 {
461 schedule(ACTION_DISCONNECT);
462 this.session = session;
463 this.alarms = alarms;
464 session.addListener(this);
465 setConnectionStatus(ConnectionStatus.CS_CONNECTED, session.getServerAddress());
466 statusNotification(ConnectionStatus.CS_CONNECTED, session.getServerAddress());
467 if (alarms != null)
468 {
469 long id = -1;
470 Alarm alarm = null;
471 // Find the newest alarm received when we were offline
472 for (Alarm itAlarm : alarms.values())
473 if (itAlarm.getId() > id)
474 {
475 alarm = itAlarm;
476 id = itAlarm.getId();
477 }
478 if (alarm != null && alarm.getId() > lastAlarmIdNotified)
479 processAlarmChange(alarm);
480 }
481 }
482 }
483 }
484
485 /**
486 * Called by connect task or session notification listener after unsuccessful
487 * connection or disconnect
488 */
489 public void onDisconnect()
490 {
491 schedule(ACTION_CONNECT);
492 nullifySession();
493 setConnectionStatus(ConnectionStatus.CS_DISCONNECTED, "");
494 statusNotification(ConnectionStatus.CS_DISCONNECTED, "");
495 }
496
497 /**
498 * Called by connect task on error during connection
499 */
500 public void onError(String error)
501 {
502 nullifySession();
503 setConnectionStatus(ConnectionStatus.CS_ERROR, error);
504 statusNotification(ConnectionStatus.CS_ERROR, error);
505 }
506
507 /**
508 * Check for expired pending connection schedule
509 */
510 private boolean isScheduleExpired()
511 {
512 if (enabled)
513 {
514 Calendar cal = Calendar.getInstance(); // get a Calendar object with current time
515 return cal.getTimeInMillis() > sp.getLong("global.scheduler.next_activation", 0);
516 }
517 return true;
518 }
519
520 /**
521 * Gets stored time settings in minutes
522 */
523 private int getMinutes(String time)
524 {
525 String[] vals = sp.getString(time, "00:00").split(":");
526 return Integer.parseInt(vals[0]) * 60 + Integer.parseInt(vals[1]);
527 }
528
529 /**
530 * Sets the offset used to compute the next schedule
531 */
532 private void setDayOffset(Calendar cal, int minutes)
533 {
534 cal.set(Calendar.HOUR_OF_DAY, 0);
535 cal.set(Calendar.MINUTE, 0);
536 cal.set(Calendar.SECOND, 0);
537 cal.set(Calendar.MILLISECOND, 0);
538 cal.add(Calendar.MINUTE, minutes);
539 }
540
541 /**
542 * Schedule a new connection/disconnection
543 */
544 public void schedule(String action)
545 {
546 Log.i(TAG, "Schedule: " + action);
547 if (!enabled)
548 cancelSchedule();
549 else
550 {
551 Calendar cal = Calendar.getInstance(); // get a Calendar object with current time
552 if (action == ACTION_RESCHEDULE)
553 cal.add(Calendar.MINUTE, schedulerPostpone);
554 if (action == ACTION_DISCONNECT)
555 cal.add(Calendar.MINUTE, schedulerDuration);
556 else if (!schedulerDailyEnabled)
557 cal.add(Calendar.MINUTE, schedulerInterval);
558 else
559 {
560 int on = schedulerDailyOn;
561 int off = schedulerDailyOff;
562 if (off < on)
563 off += ONE_DAY_MINUTES; // Next day!
564 Calendar calOn = (Calendar)cal.clone();
565 setDayOffset(calOn, on);
566 Calendar calOff = (Calendar)cal.clone();
567 setDayOffset(calOff, off);
568 cal.add(Calendar.MINUTE, schedulerInterval);
569 if (cal.before(calOn))
570 {
571 cal = (Calendar)calOn.clone();
572 Log.i(TAG, "schedule (before): rescheduled for daily interval");
573 }
574 else if (cal.after(calOff))
575 {
576 cal = (Calendar)calOn.clone();
577 setDayOffset(cal, on + ONE_DAY_MINUTES); // Move to the next activation of the excluded range
578 Log.i(TAG, "schedule (after): rescheduled for daily interval");
579 }
580 }
581 setSchedule(cal.getTimeInMillis(), action);
582 }
583 }
584
585 /**
586 * Set a connection schedule
587 */
588 private void setSchedule(long milliseconds, String action)
589 {
590 Intent intent = new Intent(this, AlarmIntentReceiver.class);
591 intent.putExtra("action", action);
592 PendingIntent sender = PendingIntent.getBroadcast(this, NETXMS_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
593 ((AlarmManager)getSystemService(ALARM_SERVICE)).set(AlarmManager.RTC_WAKEUP, milliseconds, sender);
594 // Update status
595 long last = sp.getLong("global.scheduler.next_activation", 0);
596 Editor e = sp.edit();
597 e.putLong("global.scheduler.last_activation", last);
598 e.putLong("global.scheduler.next_activation", milliseconds);
599 e.commit();
600 }
601
602 /**
603 * Cancel a pending connection schedule (if any)
604 */
605 public void cancelSchedule()
606 {
607 Intent intent = new Intent(this, AlarmIntentReceiver.class);
608 PendingIntent sender = PendingIntent.getBroadcast(this, NETXMS_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
609 ((AlarmManager)getSystemService(ALARM_SERVICE)).cancel(sender);
610 if (sp.getLong("global.scheduler.next_activation", 0) != 0)
611 {
612 Editor e = sp.edit();
613 e.putLong("global.scheduler.next_activation", 0);
614 e.commit();
615 }
616 }
617
618 /**
619 * Release internal resources nullifying current session (if any)
620 */
621 private void nullifySession()
622 {
623 synchronized (mutex)
624 {
625 if (session != null)
626 {
627 session.disconnect();
628 session.removeListener(this);
629 session = null;
630 }
631 alarms = null;
632 unknownAlarm = null;
633 }
634 }
635
636 /**
637 * Process alarm change
638 *
639 * @param alarm
640 */
641 private void processAlarmChange(Alarm alarm)
642 {
643 GenericObject object = findObjectById(alarm.getSourceObjectId());
644 synchronized (mutex)
645 {
646 if (alarms != null)
647 {
648 lastAlarmIdNotified = alarm.getId();
649 alarms.put(lastAlarmIdNotified, alarm);
650 }
651 unknownAlarm = object == null ? alarm : null;
652 alarmNotification(alarm.getCurrentSeverity(), ((object != null) ? object.getObjectName() : getString(R.string.node_unknown)) + ": " + alarm.getMessage());
653 refreshAlarmBrowser();
654 }
655 }
656
657 /**
658 * Process alarm change
659 *
660 * @param alarm
661 */
662 private void processAlarmDelete(long id)
663 {
664 synchronized (mutex)
665 {
666 if (alarms != null)
667 alarms.remove(id);
668 if (lastAlarmIdNotified == id)
669 hideNotification(NOTIFY_ALARM);
670 refreshAlarmBrowser();
671 }
672 }
673
674 /**
675 * Synchronize information about specific object in background
676 *
677 * @param objectId object ID
678 */
679 private void doBackgroundObjectSync(final long objectId)
680 {
681 new Thread("Background object sync")
682 {
683 @Override
684 public void run()
685 {
686 try
687 {
688 session.syncObjectSet(new long[] { objectId }, false, NXCSession.OBJECT_SYNC_NOTIFY);
689 }
690 catch (Exception e)
691 {
692 Log.d(TAG, "Exception in doBackgroundObjectSync", e);
693 }
694 }
695 }.start();
696 }
697
698 /**
699 * @param objectId
700 * @return
701 */
702 public GenericObject findObjectById(long objectId)
703 {
704 return findObjectById(objectId, GenericObject.class);
705 }
706
707 /**
708 * Find object by ID with class filter
709 *
710 * @param objectId
711 * @param classFilter
712 * @return
713 */
714 public GenericObject findObjectById(long objectId, Class<? extends GenericObject> classFilter)
715 {
716 // we can't search without active session
717 if (session == null)
718 return null;
719
720 GenericObject object = session.findObjectById(objectId, classFilter);
721 // if we don't have object - probably we never synced it
722 // request object synchronization in that case
723 if (object == null)
724 {
725 doBackgroundObjectSync(objectId);
726 }
727 return object;
728 }
729
730 /**
731 * @param object
732 */
733 private void processObjectUpdate(GenericObject object)
734 {
735 synchronized (mutex)
736 {
737 if (unknownAlarm != null && unknownAlarm.getSourceObjectId() == object.getObjectId()) // Update <Unknown> notification
738 {
739 alarmNotification(unknownAlarm.getCurrentSeverity(), object.getObjectName() + ": " + unknownAlarm.getMessage());
740 unknownAlarm = null;
741 }
742 refreshHomeScreen();
743 refreshAlarmBrowser();
744 refreshNodeBrowser();
745 refreshDashboardBrowser();
746 }
747 }
748
749 /**
750 * Refresh homescreen activity
751 */
752 private void refreshHomeScreen()
753 {
754 if (homeScreen != null)
755 {
756 homeScreen.runOnUiThread(new Runnable()
757 {
758 @Override
759 public void run()
760 {
761 homeScreen.refreshActivityStatus();
762 }
763 });
764 }
765 }
766
767 /**
768 * Refresh the alarms related activities
769 */
770 private void refreshAlarmBrowser()
771 {
772 if (alarmBrowser != null)
773 {
774 alarmBrowser.runOnUiThread(new Runnable()
775 {
776 @Override
777 public void run()
778 {
779 alarmBrowser.refreshList();
780 }
781 });
782 }
783 if (homeScreen != null)
784 {
785 homeScreen.runOnUiThread(new Runnable()
786 {
787 @Override
788 public void run()
789 {
790 homeScreen.refreshPendingAlarms();
791 }
792 });
793 }
794 for (Loader<Alarm[]> l : alarmLoaders)
795 l.forceLoad();
796 }
797
798 /**
799 * Refresh the node browser activity
800 */
801 private void refreshNodeBrowser()
802 {
803 if (nodeBrowser != null)
804 {
805 nodeBrowser.runOnUiThread(new Runnable()
806 {
807 @Override
808 public void run()
809 {
810 nodeBrowser.refreshList();
811 }
812 });
813 }
814 for (Loader<DciValue[]> l : dciValueLoaders)
815 l.forceLoad();
816 for (Loader<GenericObject> l : genericObjectLoaders)
817 l.forceLoad();
818 for (Loader<Set<GenericObject>> l : genericObjectChildrenLoaders)
819 l.forceLoad();
820 }
821 /**
822 * Refresh dashboard browser activity
823 */
824 private void refreshDashboardBrowser()
825 {
826 if (dashboardBrowser != null)
827 {
828 dashboardBrowser.runOnUiThread(new Runnable()
829 {
830 @Override
831 public void run()
832 {
833 dashboardBrowser.refreshList();
834 }
835 });
836 }
837 }
838
839 /**
840 * Process graph update event
841 */
842 private void processGraphUpdate()
843 {
844 synchronized (mutex)
845 {
846 if (this.graphBrowser != null)
847 {
848 graphBrowser.runOnUiThread(new Runnable()
849 {
850 @Override
851 public void run()
852 {
853 graphBrowser.refreshList();
854 }
855 });
856 }
857 }
858 }
859
860 /**
861 * Get alarm sound based on alarm severity
862 *
863 * @param severity
864 */
865 private String GetAlarmSound(int severity)
866 {
867 switch (severity)
868 {
869 case 0: // Normal
870 return sp.getString("alarm.sound.normal", "");
871 case 1: // Warning
872 return sp.getString("alarm.sound.warning", "");
873 case 2: // Minor
874 return sp.getString("alarm.sound.minor", "");
875 case 3: // Major
876 return sp.getString("alarm.sound.major", "");
877 case 4: // Critical
878 return sp.getString("alarm.sound.critical", "");
879 }
880 return "";
881 }
882
883 /**
884 * Get alarm icon based on alarm severity
885 *
886 * @param severity
887 */
888 private int getAlarmIcon(int severity)
889 {
890 switch (severity)
891 {
892 case 0: // Normal
893 return R.drawable.status_normal;
894 case 1: // Warning
895 return R.drawable.status_warning;
896 case 2: // Minor
897 return R.drawable.status_minor;
898 case 3: // Major
899 return R.drawable.status_major;
900 case 4: // Critical
901 return R.drawable.status_critical;
902 }
903 return android.R.drawable.stat_notify_sdcard;
904 }
905
906 /*
907 * (non-Javadoc)
908 *
909 * @see
910 * org.netxms.api.client.SessionListener#notificationHandler(org.netxms.api
911 * .client.SessionNotification)
912 */
913 @Override
914 public void notificationHandler(SessionNotification n)
915 {
916 switch (n.getCode())
917 {
918 case SessionNotification.CONNECTION_BROKEN:
919 case SessionNotification.SERVER_SHUTDOWN:
920 onDisconnect();
921 break;
922 case NXCNotification.NEW_ALARM:
923 case NXCNotification.ALARM_CHANGED:
924 processAlarmChange((Alarm)n.getObject());
925 break;
926 case NXCNotification.ALARM_DELETED:
927 case NXCNotification.ALARM_TERMINATED:
928 processAlarmDelete(((Alarm)n.getObject()).getId());
929 break;
930 case NXCNotification.OBJECT_CHANGED:
931 case NXCNotification.OBJECT_SYNC_COMPLETED:
932 processObjectUpdate((GenericObject)n.getObject());
933 break;
934 case NXCNotification.PREDEFINED_GRAPHS_CHANGED:
935 processGraphUpdate();
936 break;
937 default:
938 break;
939 }
940 }
941
942 /**
943 * Get list of active alarms
944 *
945 * @return list of active alarms
946 */
947 public Alarm[] getAlarms()
948 {
949 Alarm[] a;
950 synchronized (mutex)
951 {
952 if (alarms != null)
953 a = alarms.values().toArray(new Alarm[alarms.size()]);
954 else
955 a = new Alarm[0];
956 }
957 return a;
958 }
959
960 /**
961 * @param ids
962 */
963 public void acknowledgeAlarm(ArrayList<Long> ids, boolean sticky)
964 {
965 try
966 {
967 for (int i = 0; i < ids.size(); i++)
968 session.acknowledgeAlarm(ids.get(i).longValue(), sticky);
969 }
970 catch (Exception e)
971 {
972 Log.d(TAG, "Exception while executing session.acknowledgeAlarm", e);
973 }
974 }
975
976 /**
977 * @param ids
978 */
979 public void resolveAlarm(ArrayList<Long> ids)
980 {
981 try
982 {
983 for (int i = 0; i < ids.size(); i++)
984 session.resolveAlarm(ids.get(i).longValue());
985 }
986 catch (Exception e)
987 {
988 Log.d(TAG, "Exception while executing session.resolveAlarm", e);
989 }
990 }
991
992 /**
993 * @param ids
994 */
995 public void terminateAlarm(ArrayList<Long> ids)
996 {
997 try
998 {
999 for (int i = 0; i < ids.size(); i++)
1000 session.terminateAlarm(ids.get(i).longValue());
1001 }
1002 catch (Exception e)
1003 {
1004 Log.d(TAG, "Exception while executing session.terminateAlarm", e);
1005 }
1006 }
1007
1008 /**
1009 * @param id
1010 * @param state
1011 */
1012 public void setObjectMgmtState(long id, boolean state)
1013 {
1014 try
1015 {
1016 session.setObjectManaged(id, state);
1017 }
1018 catch (Exception e)
1019 {
1020 Log.d(TAG, "Exception while executing session.setObjectManaged", e);
1021 }
1022 }
1023
1024 /**
1025 *
1026 */
1027 public void loadTools()
1028 {
1029 try
1030 {
1031 this.objectTools = session.getObjectTools();
1032 }
1033 catch (Exception e)
1034 {
1035 this.objectTools = null;
1036 Log.d(TAG, "Exception while executing session.getObjectTools", e);
1037 }
1038 }
1039
1040 /**
1041 * Execute agent action. Communication with server will be done in separate worker thread.
1042 *
1043 * @param objectId
1044 * @param action
1045 */
1046 public void executeAction(long objectId, String action)
1047 {
1048 new ExecActionTask().execute(new Object[] { session, objectId, action, this });
1049 }
1050
1051 /**
1052 * @return
1053 */
1054 public List<ObjectTool> getTools()
1055 {
1056 return this.objectTools;
1057 }
1058
1059 /**
1060 * @param homeScreen
1061 */
1062 public void registerHomeScreen(HomeScreen homeScreen)
1063 {
1064 this.homeScreen = homeScreen;
1065 }
1066
1067 /**
1068 * @param browser
1069 */
1070 public void registerAlarmBrowser(AlarmBrowser browser)
1071 {
1072 alarmBrowser = browser;
1073 }
1074
1075 /**
1076 * @param browser
1077 */
1078 public void registerNodeBrowser(NodeBrowser browser)
1079 {
1080 nodeBrowser = browser;
1081 }
1082
1083 /**
1084 * @param browser
1085 */
1086 public void registerGraphBrowser(GraphBrowser browser)
1087 {
1088 graphBrowser = browser;
1089 }
1090
1091 /**
1092 * @param browser
1093 */
1094 public void registerDashboardBrowser(DashboardBrowser browser)
1095 {
1096 dashboardBrowser = browser;
1097 }
1098
1099 public void registerAlarmLoader(Loader<Alarm[]> loader)
1100 {
1101 if (!alarmLoaders.contains(this))
1102 alarmLoaders.add(loader);
1103 }
1104
1105 public void unregisterAlarmLoader(Loader<Alarm[]> loader)
1106 {
1107 alarmLoaders.remove(loader);
1108 }
1109
1110 public void registerDciValueLoader(Loader<DciValue[]> loader)
1111 {
1112 if (!dciValueLoaders.contains(this))
1113 dciValueLoaders.add(loader);
1114 }
1115
1116 public void unregisterDciValueLoader(Loader<DciValue[]> loader)
1117 {
1118 dciValueLoaders.remove(loader);
1119 }
1120
1121 public void registerGenericObjectLoader(Loader<GenericObject> loader)
1122 {
1123 if (!genericObjectLoaders.contains(this))
1124 genericObjectLoaders.add(loader);
1125 }
1126
1127 public void unregisterGenericObjectLoader(Loader<GenericObject> loader)
1128 {
1129 genericObjectLoaders.remove(loader);
1130 }
1131
1132 public void registerGenericObjectChildrenLoader(Loader<Set<GenericObject>> loader)
1133 {
1134 if (!genericObjectChildrenLoaders.contains(this))
1135 genericObjectChildrenLoaders.add(loader);
1136 }
1137
1138 public void unregisterGenericObjectChildrenLoader(Loader<Set<GenericObject>> loader)
1139 {
1140 genericObjectChildrenLoaders.remove(loader);
1141 }
1142
1143 /**
1144 * @return the connectionStatus
1145 */
1146 public ConnectionStatus getConnectionStatus()
1147 {
1148 return connectionStatus;
1149 }
1150
1151 /**
1152 * @return the connectionStatusText
1153 */
1154 public String getConnectionStatusText()
1155 {
1156 return connectionStatusText;
1157 }
1158
1159 /**
1160 * @return the connectionStatusColor
1161 */
1162 public int getConnectionStatusColor()
1163 {
1164 return connectionStatusColor;
1165 }
1166
1167 /**
1168 * @param connectionStatus
1169 * the connectionStatus to set
1170 */
1171 public void setConnectionStatus(ConnectionStatus connectionStatus, String extra)
1172 {
1173 Resources r = getResources();
1174 this.connectionStatus = connectionStatus;
1175 switch (connectionStatus)
1176 {
1177 case CS_NOCONNECTION:
1178 connectionStatusText = getString(R.string.notify_no_connection);
1179 connectionStatusColor = r.getColor(R.color.notify_no_connection);
1180 break;
1181 case CS_INPROGRESS:
1182 connectionStatusText = getString(R.string.notify_connecting);
1183 connectionStatusColor = r.getColor(R.color.notify_connecting);
1184 break;
1185 case CS_ALREADYCONNECTED:
1186 connectionStatusText = getString(R.string.notify_connected, extra);
1187 connectionStatusColor = r.getColor(R.color.notify_connected);
1188 break;
1189 case CS_CONNECTED:
1190 connectionStatusText = getString(R.string.notify_connected, extra);
1191 connectionStatusColor = r.getColor(R.color.notify_connected);
1192 break;
1193 case CS_DISCONNECTED:
1194 connectionStatusText = getString(R.string.notify_disconnected) + getNextConnectionRetry();
1195 connectionStatusColor = r.getColor(R.color.notify_disconnected);
1196 break;
1197 case CS_ERROR:
1198 connectionStatusText = getString(R.string.notify_connection_failed, extra);
1199 connectionStatusColor = r.getColor(R.color.notify_connection_failed);
1200 break;
1201 default:
1202 break;
1203 }
1204 if (homeScreen != null)
1205 {
1206 homeScreen.runOnUiThread(new Runnable()
1207 {
1208 @Override
1209 public void run()
1210 {
1211 homeScreen.setStatusText(connectionStatusText, connectionStatusColor);
1212 homeScreen.refreshActivityStatus();
1213 }
1214 });
1215 }
1216 }
1217
1218 public String getNextConnectionRetry()
1219 {
1220 long next = sp.getLong("global.scheduler.next_activation", 0);
1221 if (next != 0)
1222 {
1223 Calendar cal = Calendar.getInstance(); // get a Calendar object with current time
1224 if (cal.getTimeInMillis() < next)
1225 {
1226 cal.setTimeInMillis(next);
1227 return " " + getString(R.string.notify_next_connection_schedule, DateFormat.getDateTimeInstance().format(cal));
1228 }
1229 }
1230 return "";
1231 }
1232
1233 /**
1234 * @return the session
1235 */
1236 public NXCSession getSession()
1237 {
1238 return session;
1239 }
1240
1241 /**
1242 * Show toast with given text
1243 *
1244 * @param text message text
1245 */
1246 public void showToast(final String text)
1247 {
1248 uiThreadHandler.post(new Runnable()
1249 {
1250 @Override
1251 public void run()
1252 {
1253 Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
1254 }
1255 });
1256 }
1257 }