build number added; android client and agent switched to libs version
authorVictor Kirhenshtein <victor@netxms.org>
Sun, 10 Nov 2013 15:35:45 +0000 (17:35 +0200)
committerVictor Kirhenshtein <victor@netxms.org>
Sun, 10 Nov 2013 15:35:45 +0000 (17:35 +0200)
1.2.10; custom build number generation step removed from android builds

38 files changed:
android/src/agent/.classpath
android/src/agent/.externalToolBuilders/Generate build_number.xml from SVN revision.launch [deleted file]
android/src/agent/.externalToolBuilders/build_number.xml [deleted file]
android/src/agent/.externalToolBuilders/generate_build.pl [deleted file]
android/src/agent/.externalToolBuilders/generate_build_number.xml [deleted file]
android/src/agent/.externalToolBuilders/run.exe [deleted file]
android/src/agent/.project
android/src/agent/src/org/netxms/agent/android/service/AgentConnectorService.java
android/src/console/.classpath
android/src/console/.externalToolBuilders/Generate build_number.xml from SVN revision.launch [deleted file]
android/src/console/.externalToolBuilders/build_number.xml [deleted file]
android/src/console/.externalToolBuilders/generate_build.pl [deleted file]
android/src/console/.externalToolBuilders/generate_build_number.xml [deleted file]
android/src/console/.externalToolBuilders/run.exe [deleted file]
android/src/console/.project
android/src/console/src/org/netxms/ui/android/loaders/AlarmLoader.java
android/src/console/src/org/netxms/ui/android/loaders/DciValueLoader.java
android/src/console/src/org/netxms/ui/android/loaders/GenericObjectChildrenLoader.java
android/src/console/src/org/netxms/ui/android/loaders/GenericObjectLoader.java
android/src/console/src/org/netxms/ui/android/main/activities/AbstractComparisonChart.java
android/src/console/src/org/netxms/ui/android/main/activities/ConnectionPointBrowser.java
android/src/console/src/org/netxms/ui/android/main/activities/DashboardBrowser.java
android/src/console/src/org/netxms/ui/android/main/activities/DrawGraph.java
android/src/console/src/org/netxms/ui/android/main/activities/GraphBrowser.java
android/src/console/src/org/netxms/ui/android/main/activities/NodeBrowser.java
android/src/console/src/org/netxms/ui/android/main/dashboards/elements/AbstractDashboardElement.java
android/src/console/src/org/netxms/ui/android/main/dashboards/elements/LineChartElement.java
android/src/console/src/org/netxms/ui/android/service/ClientConnectorService.java
android/src/console/src/org/netxms/ui/android/service/tasks/ConnectTask.java
build/build_number [new file with mode: 0644]
build/next_build_number.pl [new file with mode: 0644]
include/build.h [new file with mode: 0644]
include/netxms-version.h
src/agent/core/nxagentd.cpp
src/java/build/pack.cmd
src/java/build/set_build_number.cmd [new file with mode: 0644]
src/libnetxms/seh.cpp
src/server/core/main.cpp

index 4a9c9df..1c01ad0 100644 (file)
@@ -6,7 +6,7 @@
        <classpathentry kind="lib" path="libs/acra-4.4.0.jar"/>
        <classpathentry kind="src" path="src"/>
        <classpathentry kind="src" path="gen"/>
-       <classpathentry kind="lib" path="libs/netxms-base-1.2.9.jar"/>
-       <classpathentry kind="lib" path="libs/netxms-mobile-agent-1.2.9.jar"/>
+       <classpathentry kind="lib" path="libs/netxms-base-1.2.10.jar"/>
+       <classpathentry kind="lib" path="libs/netxms-mobile-agent-1.2.10.jar"/>
        <classpathentry kind="output" path="bin/classes"/>
 </classpath>
diff --git a/android/src/agent/.externalToolBuilders/Generate build_number.xml from SVN revision.launch b/android/src/agent/.externalToolBuilders/Generate build_number.xml from SVN revision.launch
deleted file mode 100644 (file)
index c16d2e4..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
-<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>
-<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
-<booleanAttribute key="org.eclipse.debug.core.capture_output" value="false"/>
-<booleanAttribute key="org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON" value="false"/>
-<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="android-agent"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/android-agent/.externalToolBuilders/generate_build_number.xml}"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>
-<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/android-agent/.externalToolBuilders}"/>
-</launchConfiguration>
diff --git a/android/src/agent/.externalToolBuilders/build_number.xml b/android/src/agent/.externalToolBuilders/build_number.xml
deleted file mode 100644 (file)
index b8a19a9..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-       <string name="build_number">@revision@</string>
-</resources>
diff --git a/android/src/agent/.externalToolBuilders/generate_build.pl b/android/src/agent/.externalToolBuilders/generate_build.pl
deleted file mode 100644 (file)
index 833b786..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/perl
-
-$version = `svnversion`;
-chomp $version;
-if ($version =~ /[0-9]+\:([0-9]+)/)
-{
-       $version = $1;
-}
-print "Setting build number to $version\n";
-
-open(OUT, ">build_number.xml") || die "out: $!";
-
-print OUT "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
-print OUT "<resources>\n";
-print OUT "\t<string name=\"build_number\">$version</string>\n";
-print OUT "</resources>\n";
-
-close OUT;
-exit 0;
diff --git a/android/src/agent/.externalToolBuilders/generate_build_number.xml b/android/src/agent/.externalToolBuilders/generate_build_number.xml
deleted file mode 100644 (file)
index 9174e31..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<project name="Build Script" default="make" basedir=".">
-    <target name="make">
-        <!-- Get current working directory. -->
-        <exec executable="pwd" outputproperty="dir.root" />
-        <!-- Get subversion revision number. -->
-        <exec executable="svnversion" outputproperty="repository.revision" />
-        <echo message="Repository revision is ${repository.revision}" />
-
-               <copy overwrite="true" file="build_number.xml" toFile="../res/values/build_number.xml">
-                       <filterset>
-                               <filter token="revision" value="${repository.revision}" />
-                       </filterset>
-               </copy>
-    </target>
-</project>
diff --git a/android/src/agent/.externalToolBuilders/run.exe b/android/src/agent/.externalToolBuilders/run.exe
deleted file mode 100644 (file)
index d065ee0..0000000
Binary files a/android/src/agent/.externalToolBuilders/run.exe and /dev/null differ
index fab7f3c..e3a5222 100644 (file)
@@ -5,16 +5,6 @@
        <projects>
        </projects>
        <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
-                       <triggers>full,incremental,</triggers>
-                       <arguments>
-                               <dictionary>
-                                       <key>LaunchConfigHandle</key>
-                                       <value>&lt;project&gt;/.externalToolBuilders/Generate build_number.xml from SVN revision.launch</value>
-                               </dictionary>
-                       </arguments>
-               </buildCommand>
                <buildCommand>
                        <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
                        <arguments>
index dac760c..6204aa3 100644 (file)
-/**\r
- * \r
- */\r
-package org.netxms.agent.android.service;\r
-\r
-import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.Calendar;\r
-import java.util.Date;\r
-import java.util.List;\r
-\r
-import org.netxms.agent.android.R;\r
-import org.netxms.agent.android.helpers.DeviceInfoHelper;\r
-import org.netxms.agent.android.helpers.NetHelper;\r
-import org.netxms.agent.android.helpers.SafeParser;\r
-import org.netxms.agent.android.helpers.TimeHelper;\r
-import org.netxms.agent.android.main.activities.HomeScreen;\r
-import org.netxms.agent.android.receivers.AlarmIntentReceiver;\r
-import org.netxms.agent.android.service.helpers.AndroidLoggingFacility;\r
-import org.netxms.base.GeoLocation;\r
-import org.netxms.base.Logger;\r
-import org.netxms.mobile.agent.MobileAgentException;\r
-import org.netxms.mobile.agent.Session;\r
-\r
-import android.app.AlarmManager;\r
-import android.app.PendingIntent;\r
-import android.app.Service;\r
-import android.content.BroadcastReceiver;\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.content.IntentFilter;\r
-import android.content.SharedPreferences;\r
-import android.content.SharedPreferences.Editor;\r
-import android.location.Criteria;\r
-import android.location.Location;\r
-import android.location.LocationListener;\r
-import android.location.LocationManager;\r
-import android.os.AsyncTask;\r
-import android.os.Binder;\r
-import android.os.Bundle;\r
-import android.os.Handler;\r
-import android.os.IBinder;\r
-import android.preference.PreferenceManager;\r
-import android.util.Log;\r
-import android.widget.Toast;\r
-\r
-/**\r
- * Background communication service for NetXMS agent.\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- * \r
- */\r
-\r
-public class AgentConnectorService extends Service implements LocationListener\r
-{\r
-       public enum ConnectionStatus\r
-       {\r
-               CS_NOCONNECTION, CS_INPROGRESS, CS_ALREADYCONNECTED, CS_CONNECTED, CS_DISCONNECTED, CS_ERROR\r
-       };\r
-\r
-       public static final String ACTION_CONNECT = "org.netxms.agent.android.ACTION_CONNECT";\r
-       public static final String ACTION_FORCE_CONNECT = "org.netxms.agent.android.ACTION_FORCE_CONNECT";\r
-       public static final String ACTION_SCHEDULE = "org.netxms.agent.android.ACTION_SCHEDULE";\r
-       public static final String ACTION_CONFIGURE = "org.netxms.agent.android.ACTION_CONFIGURE";\r
-\r
-       public static boolean gettingNewLocation = false;\r
-\r
-       private static final String TAG = "nxagent/AgentConnectorService";\r
-       private static final int ONE_DAY_MINUTES = 24 * 60;\r
-       private static final int NETXMS_REQUEST_CODE = 123456;\r
-       private static final int STRATEGY_NET_ONLY = 0;\r
-       private static final int STRATEGY_GPS_ONLY = 1;\r
-       private static final int STRATEGY_NET_AND_GPS = 2;\r
-\r
-       private final Binder binder = new AgentConnectorBinder();\r
-       private Handler uiThreadHandler = null;\r
-       private Handler locationHandler = null;\r
-       private ConnectionStatus connectionStatus = ConnectionStatus.CS_DISCONNECTED;\r
-       private BroadcastReceiver receiver = null;\r
-       private SharedPreferences sp;\r
-       private LocationManager locationManager = null;\r
-       private HomeScreen homeScreen = null;\r
-       private boolean sendDeviceSystemInfo = true;\r
-       private boolean agentActive;\r
-       private boolean notifyToast;\r
-       private String connectionServer;\r
-       private int connectionPort;\r
-       private String connectionLogin;\r
-       private String connectionPassword;\r
-       private boolean connectionEncrypt;\r
-       private boolean schedulerDaily;\r
-       private int connectionInterval;\r
-       private boolean locationForce;\r
-       private int locationInterval;\r
-       private int locationDuration;\r
-       private int locationStrategy;\r
-       private String locationProvider = "";\r
-       private String allowedProviders = "";\r
-\r
-       /**\r
-        * Class for clients to access. Because we know this service always runs in\r
-        * the same process as its clients, we don't need to deal with IPC.\r
-        */\r
-       public class AgentConnectorBinder extends Binder\r
-       {\r
-               public AgentConnectorService getService()\r
-               {\r
-                       return AgentConnectorService.this;\r
-               }\r
-       }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * \r
-        * @see android.app.Service#onCreate()\r
-        */\r
-       @Override\r
-       public void onCreate()\r
-       {\r
-               super.onCreate();\r
-               uiThreadHandler = new Handler(getMainLooper());\r
-               showToast(getString(R.string.notify_started));\r
-\r
-               sp = PreferenceManager.getDefaultSharedPreferences(this);\r
-               Logger.setLoggingFacility(new AndroidLoggingFacility());\r
-               locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);\r
-               configure();\r
-\r
-               receiver = new BroadcastReceiver()\r
-               {\r
-                       @Override\r
-                       public void onReceive(Context context, Intent intent)\r
-                       {\r
-                               Intent i = new Intent(context, AgentConnectorService.class);\r
-                               i.setAction(ACTION_SCHEDULE);\r
-                               context.startService(i);\r
-                       }\r
-               };\r
-               registerReceiver(receiver, new IntentFilter(Intent.ACTION_TIME_TICK));\r
-       }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * \r
-        * @see android.app.Service#onStartCommand(android.content.Intent, int, int)\r
-        */\r
-       @Override\r
-       public int onStartCommand(Intent intent, int flags, int startId)\r
-       {\r
-               if ((intent != null) && (intent.getAction() != null))\r
-               {\r
-                       Log.i(TAG, "onStartCommand: " + intent.getAction());\r
-                       if (intent.getAction().equals(ACTION_CONNECT))\r
-                               reconnect(false);\r
-                       else if (intent.getAction().equals(ACTION_FORCE_CONNECT))\r
-                       {\r
-                               sendDeviceSystemInfo = true;\r
-                               reconnect(true);\r
-                       }\r
-                       else if (intent.getAction().equals(ACTION_CONFIGURE))\r
-                       {\r
-                               sendDeviceSystemInfo = true;\r
-                               configure();\r
-                               reconnect(true);\r
-                       }\r
-               }\r
-               return super.onStartCommand(intent, flags, startId);\r
-       }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * \r
-        * @see android.app.Service#onBind(android.content.Intent)\r
-        */\r
-       @Override\r
-       public IBinder onBind(Intent intent)\r
-       {\r
-               return binder;\r
-       }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * \r
-        * @see android.app.Service#onDestroy()\r
-        */\r
-       @Override\r
-       public void onDestroy()\r
-       {\r
-               super.onDestroy();\r
-       }\r
-\r
-       /**\r
-        * Configure background service\r
-        */\r
-       void configure()\r
-       {\r
-               updateLocationStatus("");\r
-               refreshHomeScreen();\r
-               agentActive = sp.getBoolean("global.activate", false);\r
-               notifyToast = sp.getBoolean("notification.toast", true);\r
-               connectionServer = sp.getString("connection.server", "");\r
-               connectionPort = SafeParser.parseInt(sp.getString("connection.port", "4747"), 4747);\r
-               connectionLogin = sp.getString("connection.login", "");\r
-               connectionPassword = sp.getString("connection.password", "");\r
-               connectionEncrypt = sp.getBoolean("connection.encrypt", false);\r
-               connectionInterval = SafeParser.parseInt(sp.getString("connection.interval", "15"), 15) * 60 * 1000;\r
-               schedulerDaily = sp.getBoolean("scheduler.daily.enable", false);\r
-               locationStrategy = SafeParser.parseInt(sp.getString("location.strategy", "0"), 0);\r
-               locationInterval = SafeParser.parseInt(sp.getString("location.interval", "30"), 30) * 60 * 1000;\r
-               locationDuration = SafeParser.parseInt(sp.getString("location.duration", "2"), 2) * 60 * 1000;\r
-               locationForce = sp.getBoolean("location.force", false);\r
-               if (locationHandler != null)\r
-                       locationHandler.removeCallbacks(locationTask);\r
-               if (locationManager != null)\r
-                       if (agentActive)\r
-                       {\r
-                               if (locationHandler == null)\r
-                                       locationHandler = new Handler(getMainLooper());\r
-                               if (locationHandler != null)\r
-                               {\r
-                                       AgentConnectorService.gettingNewLocation = false;\r
-                                       locationProvider = "";\r
-                                       locationHandler.post(locationTask);\r
-                               }\r
-                       }\r
-                       else\r
-                               locationManager.removeUpdates(AgentConnectorService.this);\r
-       }\r
-\r
-       /**\r
-        * Shutdown background service\r
-        */\r
-       public void shutdown()\r
-       {\r
-               updateLocationStatus("");\r
-               cancelConnectionSchedule();\r
-               unregisterReceiver(receiver);\r
-               stopSelf();\r
-       }\r
-\r
-       /**\r
-        * Show status notification\r
-        * \r
-        * @param status        connection status\r
-        * @param extra extra text to add at the end of the toast\r
-        */\r
-       public void statusNotification(ConnectionStatus status, String extra)\r
-       {\r
-               connectionStatus = status;\r
-               String text = "";\r
-               switch (status)\r
-               {\r
-                       case CS_CONNECTED:\r
-                               text = getString(R.string.notify_connected, extra);\r
-                               break;\r
-                       case CS_ERROR:\r
-                               text = getString(R.string.notify_connection_failed, extra);\r
-                               break;\r
-                       case CS_NOCONNECTION:\r
-                       case CS_INPROGRESS:\r
-                       case CS_ALREADYCONNECTED:\r
-                       default:\r
-                               return;\r
-               }\r
-               if (notifyToast)\r
-                       showToast(text);\r
-       }\r
-\r
-       /**\r
-        * Reconnect to server.\r
-        * \r
-        * @param force if set to true forces reconnection bypassing the scheduler\r
-        */\r
-       public void reconnect(boolean force)\r
-       {\r
-               if (agentActive && (force || isConnectionScheduleExpired()) &&\r
-                               connectionStatus != ConnectionStatus.CS_INPROGRESS &&\r
-                               connectionStatus != ConnectionStatus.CS_CONNECTED)\r
-               {\r
-                       new PushDataTask(connectionServer, connectionPort,\r
-                                       DeviceInfoHelper.getDeviceId(getApplicationContext()),\r
-                                       connectionLogin, connectionPassword, connectionEncrypt).execute();\r
-               }\r
-       }\r
-\r
-       /**\r
-        * @param homeScreen\r
-        */\r
-       public void registerHomeScreen(HomeScreen homeScreen)\r
-       {\r
-               this.homeScreen = homeScreen;\r
-       }\r
-\r
-       /**\r
-        * Show toast with given text\r
-        * \r
-        * @param text message text\r
-        */\r
-       public void showToast(final String text)\r
-       {\r
-               if (uiThreadHandler != null)\r
-                       uiThreadHandler.post(new Runnable()\r
-                       {\r
-                               @Override\r
-                               public void run()\r
-                               {\r
-                                       Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();\r
-                               }\r
-                       });\r
-       }\r
-\r
-       /**\r
-        * Refresh homescreen  activity\r
-        */\r
-       private void refreshHomeScreen()\r
-       {\r
-               if (homeScreen != null)\r
-               {\r
-                       homeScreen.runOnUiThread(new Runnable()\r
-                       {\r
-                               @Override\r
-                               public void run()\r
-                               {\r
-                                       homeScreen.refreshStatus();\r
-                               }\r
-                       });\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Gets stored time settings in minutes\r
-        */\r
-       private int getMinutes(String time)\r
-       {\r
-               String[] vals = sp.getString(time, "00:00").split(":");\r
-               return Integer.parseInt(vals[0]) * 60 + Integer.parseInt(vals[1]);\r
-       }\r
-\r
-       /**\r
-        * Sets the offset used to compute the next schedule\r
-        */\r
-       private void setDayOffset(Calendar cal, int minutes)\r
-       {\r
-               cal.set(Calendar.HOUR_OF_DAY, 0);\r
-               cal.set(Calendar.MINUTE, 0);\r
-               cal.set(Calendar.SECOND, 0);\r
-               cal.set(Calendar.MILLISECOND, 0);\r
-               cal.add(Calendar.MINUTE, minutes);\r
-       }\r
-\r
-       /**\r
-        * Get the next schedule based on daily interval, if set\r
-        * \r
-        * @param interval      expected schedule in milliseconds\r
-        * @return new schedule in milliseconds adjusted as necessary\r
-        */\r
-       private long getNextSchedule(int interval)\r
-       {\r
-               if (!schedulerDaily)\r
-                       return interval;\r
-               Calendar cal = Calendar.getInstance(); // get a Calendar object with current time\r
-               long now = cal.getTimeInMillis();\r
-               int on = getMinutes("scheduler.daily.on");\r
-               int off = getMinutes("scheduler.daily.off");\r
-               if (off < on)\r
-                       off += ONE_DAY_MINUTES; // Next day!\r
-               Calendar calOn = (Calendar)cal.clone();\r
-               setDayOffset(calOn, on);\r
-               Calendar calOff = (Calendar)cal.clone();\r
-               setDayOffset(calOff, off);\r
-               cal.add(Calendar.MILLISECOND, interval);\r
-               if (cal.before(calOn))\r
-               {\r
-                       cal = (Calendar)calOn.clone();\r
-                       Log.i(TAG, "Rescheduled for daily interval (before 'on')");\r
-               }\r
-               else if (cal.after(calOff))\r
-               {\r
-                       cal = (Calendar)calOn.clone();\r
-                       setDayOffset(cal, on + ONE_DAY_MINUTES); // Move to the next activation of the excluded range\r
-                       Log.i(TAG, "Rescheduled for daily interval (after 'off')");\r
-               }\r
-               Log.i(TAG, "Next schedule in " + (cal.getTimeInMillis() - now) / 1000 + " seconds");\r
-               return cal.getTimeInMillis() - now;\r
-       }\r
-\r
-       /**\r
-        * Check for expired pending connection schedule\r
-        */\r
-       private boolean isConnectionScheduleExpired()\r
-       {\r
-               Calendar cal = Calendar.getInstance(); // get a Calendar object with current time\r
-               return cal.getTimeInMillis() > sp.getLong("scheduler.next_activation", 0);\r
-       }\r
-\r
-       /**\r
-        * Set a connection schedule\r
-        * \r
-        * @param milliseconds  time for new schedule\r
-        */\r
-       private void setConnectionSchedule(long milliseconds)\r
-       {\r
-               Log.i(TAG, "setSchedule to: " + TimeHelper.getTimeString(milliseconds));\r
-               Intent intent = new Intent(this, AlarmIntentReceiver.class);\r
-               PendingIntent sender = PendingIntent.getBroadcast(this, NETXMS_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);\r
-               ((AlarmManager)getSystemService(ALARM_SERVICE)).set(AlarmManager.RTC_WAKEUP, milliseconds, sender);\r
-               Editor e = sp.edit();\r
-               e.putLong("scheduler.next_activation", milliseconds);\r
-               e.commit();\r
-       }\r
-\r
-       /**\r
-        * Cancel a pending connection schedule (if any)\r
-        */\r
-       private void cancelConnectionSchedule()\r
-       {\r
-               Log.i(TAG, "cancelSchedule");\r
-               Intent intent = new Intent(this, AlarmIntentReceiver.class);\r
-               PendingIntent sender = PendingIntent.getBroadcast(this, NETXMS_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);\r
-               ((AlarmManager)getSystemService(ALARM_SERVICE)).cancel(sender);\r
-               Editor e = sp.edit();\r
-               e.putLong("scheduler.next_activation", 0);\r
-               e.commit();\r
-       }\r
-\r
-       /**\r
-        * Get flags. Currently not used.\r
-        */\r
-       private int getFlags()\r
-       {\r
-               return 0;\r
-       }\r
-\r
-       /**\r
-        * Internal task for connecting and pushing data\r
-        */\r
-       private class PushDataTask extends AsyncTask<Object, Void, Boolean>\r
-       {\r
-               private final String server;\r
-               private final Integer port;\r
-               private final String deviceId;\r
-               private final String login;\r
-               private final String password;\r
-               private final boolean encrypt;\r
-               private String connMsg = "";\r
-\r
-               protected PushDataTask(String server, Integer port, String deviceId, String login, String password, boolean encrypt)\r
-               {\r
-                       this.server = server;\r
-                       this.port = port;\r
-                       this.deviceId = deviceId;\r
-                       this.login = login;\r
-                       this.password = password;\r
-                       this.encrypt = encrypt;\r
-               }\r
-\r
-               @Override\r
-               protected Boolean doInBackground(Object... params)\r
-               {\r
-                       Log.d(TAG, "PushDataTask.doInBackground: reconnecting...");\r
-                       statusNotification(ConnectionStatus.CS_INPROGRESS, "");\r
-                       if (NetHelper.isInternetOn(getApplicationContext()))\r
-                       {\r
-                               Session session = new Session(server, port, deviceId, login, password, encrypt);\r
-                               try\r
-                               {\r
-                                       session.connect();\r
-                                       Log.v(TAG, "PushDataTask.doInBackground: connected");\r
-                                       statusNotification(ConnectionStatus.CS_CONNECTED, getString(R.string.notify_pushing_data));\r
-                                       if (sendDeviceSystemInfo)\r
-                                       {\r
-                                               Log.v(TAG, "PushDataTask.doInBackground: sending DeviceSystemInfo");\r
-                                               session.reportDeviceSystemInfo(DeviceInfoHelper.getManufacturer(), DeviceInfoHelper.getModel(),\r
-                                                               DeviceInfoHelper.getOSName(), DeviceInfoHelper.getRelease(), DeviceInfoHelper.getSerial(),\r
-                                                               DeviceInfoHelper.getUser(getApplicationContext()));\r
-                                               sendDeviceSystemInfo = false;\r
-                                       }\r
-                                       session.reportDeviceStatus(NetHelper.getInetAddress(), getGeoLocation(), getFlags(),\r
-                                                       DeviceInfoHelper.getBatteryLevel(getApplicationContext()));\r
-                                       session.disconnect();\r
-                                       Log.v(TAG, "PushDataTask.doInBackground: data transfer completed");\r
-                                       connMsg = getString(R.string.notify_connected, server + ":" + port);\r
-                                       return true;\r
-                               }\r
-                               catch (IOException e)\r
-                               {\r
-                                       Log.e(TAG, "IOException while executing PushDataTask.doInBackground on connect", e);\r
-                                       connMsg = e.getLocalizedMessage();\r
-                               }\r
-                               catch (MobileAgentException e)\r
-                               {\r
-                                       Log.e(TAG, "MobileAgentException while executing PushDataTask.doInBackground on connect", e);\r
-                                       connMsg = e.getLocalizedMessage();\r
-                               }\r
-                       }\r
-                       else\r
-                       {\r
-                               Log.w(TAG, "PushDataTask.doInBackground: no internet connection");\r
-                               connMsg = getString(R.string.notify_no_connection);\r
-                       }\r
-                       return false;\r
-               }\r
-\r
-               @Override\r
-               protected void onPostExecute(Boolean result)\r
-               {\r
-                       if (result == true)\r
-                       {\r
-                               Log.d(TAG, "PushDataTask.onPostExecute: disconnecting...");\r
-                               statusNotification(ConnectionStatus.CS_DISCONNECTED, "");\r
-                       }\r
-                       else\r
-                       {\r
-                               Log.d(TAG, "PushDataTask.onPostExecute: error: " + connMsg);\r
-                               statusNotification(ConnectionStatus.CS_ERROR, connMsg);\r
-                       }\r
-                       Editor e = sp.edit();\r
-                       e.putLong("scheduler.last_activation", Calendar.getInstance().getTimeInMillis());\r
-                       e.putString("scheduler.last_activation_msg", connMsg);\r
-                       e.commit();\r
-                       if (agentActive)\r
-                               setConnectionSchedule(Calendar.getInstance().getTimeInMillis() + getNextSchedule(connectionInterval));\r
-                       else\r
-                               cancelConnectionSchedule();\r
-                       refreshHomeScreen();\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Updates last location status\r
-        * \r
-        * @pasam status        status of last location\r
-        */\r
-       private void updateLocationStatus(String status)\r
-       {\r
-               Editor e = sp.edit();\r
-               e.putString("location.last_status", status);\r
-               e.commit();\r
-       }\r
-\r
-       /**\r
-        * Convert location provider from Android type to NetXMS type.\r
-        * \r
-        * @return Provider type\r
-        */\r
-       private int getProviderType(String provider)\r
-       {\r
-               if (provider.compareTo(LocationManager.GPS_PROVIDER) == 0)\r
-                       return GeoLocation.GPS;\r
-               else if (provider.compareTo(LocationManager.NETWORK_PROVIDER) == 0)\r
-                       return GeoLocation.NETWORK;\r
-               return GeoLocation.UNSET;\r
-       }\r
-\r
-       /**\r
-        * Get last known geolocation (depending on strategy used it could be very old).\r
-        * If no provider available, try to get a last position from any available provider\r
-        * identifyed by the system using the ACCURACY_COARSE criteria.\r
-        * \r
-        * @return Last known location or null if not known\r
-        */\r
-       private GeoLocation getGeoLocation()\r
-       {\r
-               if (locationManager != null)\r
-               {\r
-                       Location location = null;\r
-                       if (locationProvider.length() > 0) // Did we get an updated position?\r
-                               location = locationManager.getLastKnownLocation(locationProvider);\r
-                       else\r
-                       {       // Try to get it using the best provider available\r
-                               Criteria criteria = new Criteria();\r
-                               if (criteria != null)\r
-                               {\r
-                                       criteria.setAccuracy(Criteria.ACCURACY_COARSE);\r
-                                       String bestProvider = locationManager.getBestProvider(criteria, true);\r
-                                       if (bestProvider != null)\r
-                                               location = locationManager.getLastKnownLocation(bestProvider);\r
-                               }\r
-                       }\r
-                       if (location != null)\r
-                       {\r
-                               String locStatus = getString(R.string.info_location_good,\r
-                                               TimeHelper.getTimeString(location.getTime()),\r
-                                               location.getProvider(),\r
-                                               Float.toString((float)location.getLatitude()),\r
-                                               Float.toString((float)location.getLongitude()),\r
-                                               Float.toString(location.getAccuracy()));\r
-                               Log.i(TAG, locStatus);\r
-                               updateLocationStatus(locStatus);\r
-                               return new GeoLocation(location.getLatitude(),\r
-                                               location.getLongitude(),\r
-                                               getProviderType(location.getProvider()),\r
-                                               (int)location.getAccuracy(),\r
-                                               new Date(location.getTime()));\r
-                       }\r
-               }\r
-               return null;\r
-       }\r
-       /**\r
-        * Get list of enabled location provider based on selected strategy\r
-        * \r
-        * @param       strategy: provider location strategy\r
-        * @return      List of enabled provider based on selected strategy\r
-        */\r
-       private List<String> getLocationProviderList(int strategy)\r
-       {\r
-               List<String> providerList = new ArrayList<String>(0);\r
-               providerList.clear();\r
-               switch (strategy)\r
-               {\r
-                       case STRATEGY_NET_ONLY:\r
-                               if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))\r
-                                       providerList.add(LocationManager.NETWORK_PROVIDER);\r
-                               break;\r
-                       case STRATEGY_GPS_ONLY:\r
-                               if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))\r
-                                       providerList.add(LocationManager.GPS_PROVIDER);\r
-                               break;\r
-                       case STRATEGY_NET_AND_GPS:\r
-                               if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))\r
-                                       providerList.add(LocationManager.NETWORK_PROVIDER);\r
-                               if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))\r
-                                       providerList.add(LocationManager.GPS_PROVIDER);\r
-                               break;\r
-               }\r
-               return providerList;\r
-       }\r
-\r
-       /**\r
-        * Internal handler to get new location based on location strategy set\r
-        */\r
-       private final Runnable locationTask = new Runnable()\r
-       {\r
-               @Override\r
-               public void run()\r
-               {\r
-                       locationHandler.removeCallbacks(locationTask);\r
-                       if (AgentConnectorService.gettingNewLocation)\r
-                       {\r
-                               AgentConnectorService.gettingNewLocation = false;\r
-                               locationManager.removeUpdates(AgentConnectorService.this);\r
-                               locationHandler.postDelayed(locationTask, getNextSchedule(locationInterval));\r
-                               String locStatus = getString(R.string.info_location_timeout, allowedProviders);\r
-                               Log.i(TAG, locStatus);\r
-                               updateLocationStatus(locStatus);\r
-                               refreshHomeScreen();\r
-                       }\r
-                       else if (locationForce)\r
-                       {\r
-                               locationProvider = "";\r
-                               String locStatus = getString(R.string.info_location_no_provider);\r
-                               List<String> providerList = getLocationProviderList(locationStrategy);\r
-                               if (providerList.size() > 0)\r
-                               {\r
-                                       allowedProviders = "";\r
-                                       for (int i = 0; i < providerList.size(); i++)\r
-                                       {\r
-                                               allowedProviders += (i > 0 ? ", " : "") + providerList.get(i);\r
-                                               locationManager.requestLocationUpdates(providerList.get(i), 0, 0, AgentConnectorService.this); // 0, 0 to have it ASAP\r
-                                       }\r
-                                       AgentConnectorService.gettingNewLocation = true;\r
-                                       locationHandler.postDelayed(locationTask, getNextSchedule(locationDuration));\r
-                                       locStatus = getString(R.string.info_location_acquiring, allowedProviders);\r
-                               }\r
-                               else\r
-                                       locationHandler.postDelayed(locationTask, getNextSchedule(locationInterval));\r
-                               Log.i(TAG, locStatus);\r
-                               updateLocationStatus(locStatus);\r
-                               refreshHomeScreen();\r
-                       }\r
-                       else\r
-                               locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, AgentConnectorService.this); // 0, 0 to have it ASAP\r
-               }\r
-       };\r
-\r
-       @Override\r
-       public void onLocationChanged(Location location)\r
-       {\r
-               String locStatus = getString(R.string.info_location_good,\r
-                               TimeHelper.getTimeString(location.getTime()),\r
-                               locationProvider = location.getProvider(),\r
-                               Float.toString((float)location.getLatitude()),\r
-                               Float.toString((float)location.getLongitude()),\r
-                               Float.toString(location.getAccuracy()));\r
-               Log.i(TAG, locStatus);\r
-               updateLocationStatus(locStatus);\r
-               locationManager.removeUpdates(AgentConnectorService.this);\r
-               gettingNewLocation = false;\r
-               locationHandler.removeCallbacks(locationTask);\r
-               locationHandler.postDelayed(locationTask, locationInterval);\r
-               refreshHomeScreen();\r
-       }\r
-\r
-       @Override\r
-       public void onProviderDisabled(String provider)\r
-       {\r
-               Log.d(TAG, "onProviderDisabled: " + provider);\r
-       }\r
-\r
-       @Override\r
-       public void onProviderEnabled(String provider)\r
-       {\r
-               Log.d(TAG, "onProviderEnabled: " + provider);\r
-       }\r
-\r
-       @Override\r
-       public void onStatusChanged(String provider, int status, Bundle extras)\r
-       {\r
-               Log.d(TAG, "onStatusChanged: " + provider + " status: " + status);\r
-       }\r
-}\r
+/**
+ * 
+ */
+package org.netxms.agent.android.service;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import org.netxms.agent.android.R;
+import org.netxms.agent.android.helpers.DeviceInfoHelper;
+import org.netxms.agent.android.helpers.NetHelper;
+import org.netxms.agent.android.helpers.SafeParser;
+import org.netxms.agent.android.helpers.TimeHelper;
+import org.netxms.agent.android.main.activities.HomeScreen;
+import org.netxms.agent.android.receivers.AlarmIntentReceiver;
+import org.netxms.agent.android.service.helpers.AndroidLoggingFacility;
+import org.netxms.base.GeoLocation;
+import org.netxms.base.Logger;
+import org.netxms.mobile.agent.MobileAgentException;
+import org.netxms.mobile.agent.Session;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Background communication service for NetXMS agent.
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ * 
+ */
+
+public class AgentConnectorService extends Service implements LocationListener
+{
+       public enum ConnectionStatus
+       {
+               CS_NOCONNECTION, CS_INPROGRESS, CS_ALREADYCONNECTED, CS_CONNECTED, CS_DISCONNECTED, CS_ERROR
+       };
+
+       public static final String ACTION_CONNECT = "org.netxms.agent.android.ACTION_CONNECT";
+       public static final String ACTION_FORCE_CONNECT = "org.netxms.agent.android.ACTION_FORCE_CONNECT";
+       public static final String ACTION_SCHEDULE = "org.netxms.agent.android.ACTION_SCHEDULE";
+       public static final String ACTION_CONFIGURE = "org.netxms.agent.android.ACTION_CONFIGURE";
+
+       public static boolean gettingNewLocation = false;
+
+       private static final String TAG = "nxagent/AgentConnectorService";
+       private static final int ONE_DAY_MINUTES = 24 * 60;
+       private static final int NETXMS_REQUEST_CODE = 123456;
+       private static final int STRATEGY_NET_ONLY = 0;
+       private static final int STRATEGY_GPS_ONLY = 1;
+       private static final int STRATEGY_NET_AND_GPS = 2;
+
+       private final Binder binder = new AgentConnectorBinder();
+       private Handler uiThreadHandler = null;
+       private Handler locationHandler = null;
+       private ConnectionStatus connectionStatus = ConnectionStatus.CS_DISCONNECTED;
+       private BroadcastReceiver receiver = null;
+       private SharedPreferences sp;
+       private LocationManager locationManager = null;
+       private HomeScreen homeScreen = null;
+       private boolean sendDeviceSystemInfo = true;
+       private boolean agentActive;
+       private boolean notifyToast;
+       private String connectionServer;
+       private int connectionPort;
+       private String connectionLogin;
+       private String connectionPassword;
+       private boolean connectionEncrypt;
+       private boolean schedulerDaily;
+       private int connectionInterval;
+       private boolean locationForce;
+       private int locationInterval;
+       private int locationDuration;
+       private int locationStrategy;
+       private String locationProvider = "";
+       private String allowedProviders = "";
+
+       /**
+        * Class for clients to access. Because we know this service always runs in
+        * the same process as its clients, we don't need to deal with IPC.
+        */
+       public class AgentConnectorBinder extends Binder
+       {
+               public AgentConnectorService getService()
+               {
+                       return AgentConnectorService.this;
+               }
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see android.app.Service#onCreate()
+        */
+       @Override
+       public void onCreate()
+       {
+               super.onCreate();
+               uiThreadHandler = new Handler(getMainLooper());
+               showToast(getString(R.string.notify_started));
+
+               sp = PreferenceManager.getDefaultSharedPreferences(this);
+               Logger.setLoggingFacility(new AndroidLoggingFacility());
+               locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
+               configure();
+
+               receiver = new BroadcastReceiver()
+               {
+                       @Override
+                       public void onReceive(Context context, Intent intent)
+                       {
+                               Intent i = new Intent(context, AgentConnectorService.class);
+                               i.setAction(ACTION_SCHEDULE);
+                               context.startService(i);
+                       }
+               };
+               registerReceiver(receiver, new IntentFilter(Intent.ACTION_TIME_TICK));
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see android.app.Service#onStartCommand(android.content.Intent, int, int)
+        */
+       @Override
+       public int onStartCommand(Intent intent, int flags, int startId)
+       {
+               if ((intent != null) && (intent.getAction() != null))
+               {
+                       Log.i(TAG, "onStartCommand: " + intent.getAction());
+                       if (intent.getAction().equals(ACTION_CONNECT))
+                               reconnect(false);
+                       else if (intent.getAction().equals(ACTION_FORCE_CONNECT))
+                       {
+                               sendDeviceSystemInfo = true;
+                               reconnect(true);
+                       }
+                       else if (intent.getAction().equals(ACTION_CONFIGURE))
+                       {
+                               sendDeviceSystemInfo = true;
+                               configure();
+                               reconnect(true);
+                       }
+               }
+               return super.onStartCommand(intent, flags, startId);
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see android.app.Service#onBind(android.content.Intent)
+        */
+       @Override
+       public IBinder onBind(Intent intent)
+       {
+               return binder;
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see android.app.Service#onDestroy()
+        */
+       @Override
+       public void onDestroy()
+       {
+               super.onDestroy();
+       }
+
+       /**
+        * Configure background service
+        */
+       void configure()
+       {
+               updateLocationStatus("");
+               refreshHomeScreen();
+               agentActive = sp.getBoolean("global.activate", false);
+               notifyToast = sp.getBoolean("notification.toast", true);
+               connectionServer = sp.getString("connection.server", "");
+               connectionPort = SafeParser.parseInt(sp.getString("connection.port", "4747"), 4747);
+               connectionLogin = sp.getString("connection.login", "");
+               connectionPassword = sp.getString("connection.password", "");
+               connectionEncrypt = sp.getBoolean("connection.encrypt", false);
+               connectionInterval = SafeParser.parseInt(sp.getString("connection.interval", "15"), 15) * 60 * 1000;
+               schedulerDaily = sp.getBoolean("scheduler.daily.enable", false);
+               locationStrategy = SafeParser.parseInt(sp.getString("location.strategy", "0"), 0);
+               locationInterval = SafeParser.parseInt(sp.getString("location.interval", "30"), 30) * 60 * 1000;
+               locationDuration = SafeParser.parseInt(sp.getString("location.duration", "2"), 2) * 60 * 1000;
+               locationForce = sp.getBoolean("location.force", false);
+               if (locationHandler != null)
+                       locationHandler.removeCallbacks(locationTask);
+               if (locationManager != null)
+                       if (agentActive)
+                       {
+                               if (locationHandler == null)
+                                       locationHandler = new Handler(getMainLooper());
+                               if (locationHandler != null)
+                               {
+                                       AgentConnectorService.gettingNewLocation = false;
+                                       locationProvider = "";
+                                       locationHandler.post(locationTask);
+                               }
+                       }
+                       else
+                               locationManager.removeUpdates(AgentConnectorService.this);
+       }
+
+       /**
+        * Shutdown background service
+        */
+       public void shutdown()
+       {
+               updateLocationStatus("");
+               cancelConnectionSchedule();
+               unregisterReceiver(receiver);
+               stopSelf();
+       }
+
+       /**
+        * Show status notification
+        * 
+        * @param status        connection status
+        * @param extra extra text to add at the end of the toast
+        */
+       public void statusNotification(ConnectionStatus status, String extra)
+       {
+               connectionStatus = status;
+               String text = "";
+               switch (status)
+               {
+                       case CS_CONNECTED:
+                               text = getString(R.string.notify_connected, extra);
+                               break;
+                       case CS_ERROR:
+                               text = getString(R.string.notify_connection_failed, extra);
+                               break;
+                       case CS_NOCONNECTION:
+                       case CS_INPROGRESS:
+                       case CS_ALREADYCONNECTED:
+                       default:
+                               return;
+               }
+               if (notifyToast)
+                       showToast(text);
+       }
+
+       /**
+        * Reconnect to server.
+        * 
+        * @param force if set to true forces reconnection bypassing the scheduler
+        */
+       public void reconnect(boolean force)
+       {
+               if (agentActive && (force || isConnectionScheduleExpired()) &&
+                               connectionStatus != ConnectionStatus.CS_INPROGRESS &&
+                               connectionStatus != ConnectionStatus.CS_CONNECTED)
+               {
+                       new PushDataTask(connectionServer, connectionPort,
+                                       DeviceInfoHelper.getDeviceId(getApplicationContext()),
+                                       connectionLogin, connectionPassword, connectionEncrypt).execute();
+               }
+       }
+
+       /**
+        * @param homeScreen
+        */
+       public void registerHomeScreen(HomeScreen homeScreen)
+       {
+               this.homeScreen = homeScreen;
+       }
+
+       /**
+        * Show toast with given text
+        * 
+        * @param text message text
+        */
+       public void showToast(final String text)
+       {
+               if (uiThreadHandler != null)
+                       uiThreadHandler.post(new Runnable()
+                       {
+                               @Override
+                               public void run()
+                               {
+                                       Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
+                               }
+                       });
+       }
+
+       /**
+        * Refresh homescreen  activity
+        */
+       private void refreshHomeScreen()
+       {
+               if (homeScreen != null)
+               {
+                       homeScreen.runOnUiThread(new Runnable()
+                       {
+                               @Override
+                               public void run()
+                               {
+                                       homeScreen.refreshStatus();
+                               }
+                       });
+               }
+       }
+
+       /**
+        * Gets stored time settings in minutes
+        */
+       private int getMinutes(String time)
+       {
+               String[] vals = sp.getString(time, "00:00").split(":");
+               return Integer.parseInt(vals[0]) * 60 + Integer.parseInt(vals[1]);
+       }
+
+       /**
+        * Sets the offset used to compute the next schedule
+        */
+       private void setDayOffset(Calendar cal, int minutes)
+       {
+               cal.set(Calendar.HOUR_OF_DAY, 0);
+               cal.set(Calendar.MINUTE, 0);
+               cal.set(Calendar.SECOND, 0);
+               cal.set(Calendar.MILLISECOND, 0);
+               cal.add(Calendar.MINUTE, minutes);
+       }
+
+       /**
+        * Get the next schedule based on daily interval, if set
+        * 
+        * @param interval      expected schedule in milliseconds
+        * @return new schedule in milliseconds adjusted as necessary
+        */
+       private long getNextSchedule(int interval)
+       {
+               if (!schedulerDaily)
+                       return interval;
+               Calendar cal = Calendar.getInstance(); // get a Calendar object with current time
+               long now = cal.getTimeInMillis();
+               int on = getMinutes("scheduler.daily.on");
+               int off = getMinutes("scheduler.daily.off");
+               if (off < on)
+                       off += ONE_DAY_MINUTES; // Next day!
+               Calendar calOn = (Calendar)cal.clone();
+               setDayOffset(calOn, on);
+               Calendar calOff = (Calendar)cal.clone();
+               setDayOffset(calOff, off);
+               cal.add(Calendar.MILLISECOND, interval);
+               if (cal.before(calOn))
+               {
+                       cal = (Calendar)calOn.clone();
+                       Log.i(TAG, "Rescheduled for daily interval (before 'on')");
+               }
+               else if (cal.after(calOff))
+               {
+                       cal = (Calendar)calOn.clone();
+                       setDayOffset(cal, on + ONE_DAY_MINUTES); // Move to the next activation of the excluded range
+                       Log.i(TAG, "Rescheduled for daily interval (after 'off')");
+               }
+               Log.i(TAG, "Next schedule in " + (cal.getTimeInMillis() - now) / 1000 + " seconds");
+               return cal.getTimeInMillis() - now;
+       }
+
+       /**
+        * Check for expired pending connection schedule
+        */
+       private boolean isConnectionScheduleExpired()
+       {
+               Calendar cal = Calendar.getInstance(); // get a Calendar object with current time
+               return cal.getTimeInMillis() > sp.getLong("scheduler.next_activation", 0);
+       }
+
+       /**
+        * Set a connection schedule
+        * 
+        * @param milliseconds  time for new schedule
+        */
+       private void setConnectionSchedule(long milliseconds)
+       {
+               Log.i(TAG, "setSchedule to: " + TimeHelper.getTimeString(milliseconds));
+               Intent intent = new Intent(this, AlarmIntentReceiver.class);
+               PendingIntent sender = PendingIntent.getBroadcast(this, NETXMS_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+               ((AlarmManager)getSystemService(ALARM_SERVICE)).set(AlarmManager.RTC_WAKEUP, milliseconds, sender);
+               Editor e = sp.edit();
+               e.putLong("scheduler.next_activation", milliseconds);
+               e.commit();
+       }
+
+       /**
+        * Cancel a pending connection schedule (if any)
+        */
+       private void cancelConnectionSchedule()
+       {
+               Log.i(TAG, "cancelSchedule");
+               Intent intent = new Intent(this, AlarmIntentReceiver.class);
+               PendingIntent sender = PendingIntent.getBroadcast(this, NETXMS_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+               ((AlarmManager)getSystemService(ALARM_SERVICE)).cancel(sender);
+               Editor e = sp.edit();
+               e.putLong("scheduler.next_activation", 0);
+               e.commit();
+       }
+
+       /**
+        * Get flags. Currently not used.
+        */
+       private int getFlags()
+       {
+               return 0;
+       }
+
+       /**
+        * Internal task for connecting and pushing data
+        */
+       private class PushDataTask extends AsyncTask<Object, Void, Boolean>
+       {
+               private final String server;
+               private final Integer port;
+               private final String deviceId;
+               private final String login;
+               private final String password;
+               private final boolean encrypt;
+               private String connMsg = "";
+
+               protected PushDataTask(String server, Integer port, String deviceId, String login, String password, boolean encrypt)
+               {
+                       this.server = server;
+                       this.port = port;
+                       this.deviceId = deviceId;
+                       this.login = login;
+                       this.password = password;
+                       this.encrypt = encrypt;
+               }
+
+               @Override
+               protected Boolean doInBackground(Object... params)
+               {
+                       Log.d(TAG, "PushDataTask.doInBackground: reconnecting...");
+                       statusNotification(ConnectionStatus.CS_INPROGRESS, "");
+                       if (NetHelper.isInternetOn(getApplicationContext()))
+                       {
+                               Session session = new Session(server, port, deviceId, login, password, encrypt);
+                               try
+                               {
+                                       session.connect();
+                                       Log.v(TAG, "PushDataTask.doInBackground: connected");
+                                       statusNotification(ConnectionStatus.CS_CONNECTED, getString(R.string.notify_pushing_data));
+                                       if (sendDeviceSystemInfo)
+                                       {
+                                               Log.v(TAG, "PushDataTask.doInBackground: sending DeviceSystemInfo");
+                                               session.reportDeviceSystemInfo(DeviceInfoHelper.getManufacturer(), DeviceInfoHelper.getModel(),
+                                                               DeviceInfoHelper.getOSName(), DeviceInfoHelper.getRelease(), DeviceInfoHelper.getSerial(),
+                                                               DeviceInfoHelper.getUser(getApplicationContext()));
+                                               sendDeviceSystemInfo = false;
+                                       }
+                                       session.reportDeviceStatus(NetHelper.getInetAddress(), getGeoLocation(), getFlags(),
+                                                       DeviceInfoHelper.getBatteryLevel(getApplicationContext()));
+                                       session.disconnect();
+                                       Log.v(TAG, "PushDataTask.doInBackground: data transfer completed");
+                                       connMsg = getString(R.string.notify_connected, server + ":" + port);
+                                       return true;
+                               }
+                               catch (IOException e)
+                               {
+                                       Log.e(TAG, "IOException while executing PushDataTask.doInBackground on connect", e);
+                                       connMsg = e.getLocalizedMessage();
+                               }
+                               catch (MobileAgentException e)
+                               {
+                                       Log.e(TAG, "MobileAgentException while executing PushDataTask.doInBackground on connect", e);
+                                       connMsg = e.getLocalizedMessage();
+                               }
+                       }
+                       else
+                       {
+                               Log.w(TAG, "PushDataTask.doInBackground: no internet connection");
+                               connMsg = getString(R.string.notify_no_connection);
+                       }
+                       return false;
+               }
+
+               @Override
+               protected void onPostExecute(Boolean result)
+               {
+                       if (result == true)
+                       {
+                               Log.d(TAG, "PushDataTask.onPostExecute: disconnecting...");
+                               statusNotification(ConnectionStatus.CS_DISCONNECTED, "");
+                       }
+                       else
+                       {
+                               Log.d(TAG, "PushDataTask.onPostExecute: error: " + connMsg);
+                               statusNotification(ConnectionStatus.CS_ERROR, connMsg);
+                       }
+                       Editor e = sp.edit();
+                       e.putLong("scheduler.last_activation", Calendar.getInstance().getTimeInMillis());
+                       e.putString("scheduler.last_activation_msg", connMsg);
+                       e.commit();
+                       if (agentActive)
+                               setConnectionSchedule(Calendar.getInstance().getTimeInMillis() + getNextSchedule(connectionInterval));
+                       else
+                               cancelConnectionSchedule();
+                       refreshHomeScreen();
+               }
+       }
+
+       /**
+        * Updates last location status
+        * 
+        * @pasam status        status of last location
+        */
+       private void updateLocationStatus(String status)
+       {
+               Editor e = sp.edit();
+               e.putString("location.last_status", status);
+               e.commit();
+       }
+
+       /**
+        * Convert location provider from Android type to NetXMS type.
+        * 
+        * @return Provider type
+        */
+       private int getProviderType(String provider)
+       {
+               if (provider.compareTo(LocationManager.GPS_PROVIDER) == 0)
+                       return GeoLocation.GPS;
+               else if (provider.compareTo(LocationManager.NETWORK_PROVIDER) == 0)
+                       return GeoLocation.NETWORK;
+               return GeoLocation.UNSET;
+       }
+
+       /**
+        * Get last known geolocation (depending on strategy used it could be very old).
+        * If no provider available, try to get a last position from any available provider
+        * identifyed by the system using the ACCURACY_COARSE criteria.
+        * 
+        * @return Last known location or null if not known
+        */
+       private GeoLocation getGeoLocation()
+       {
+               if (locationManager != null)
+               {
+                       Location location = null;
+                       if (locationProvider.length() > 0) // Did we get an updated position?
+                               location = locationManager.getLastKnownLocation(locationProvider);
+                       else
+                       {       // Try to get it using the best provider available
+                               Criteria criteria = new Criteria();
+                               if (criteria != null)
+                               {
+                                       criteria.setAccuracy(Criteria.ACCURACY_COARSE);
+                                       String bestProvider = locationManager.getBestProvider(criteria, true);
+                                       if (bestProvider != null)
+                                               location = locationManager.getLastKnownLocation(bestProvider);
+                               }
+                       }
+                       if (location != null)
+                       {
+                               String locStatus = getString(R.string.info_location_good,
+                                               TimeHelper.getTimeString(location.getTime()),
+                                               location.getProvider(),
+                                               Float.toString((float)location.getLatitude()),
+                                               Float.toString((float)location.getLongitude()),
+                                               Float.toString(location.getAccuracy()));
+                               Log.i(TAG, locStatus);
+                               updateLocationStatus(locStatus);
+                               return new GeoLocation(location.getLatitude(),
+                                               location.getLongitude(),
+                                               getProviderType(location.getProvider()),
+                                               (int)location.getAccuracy(),
+                                               new Date(location.getTime()));
+                       }
+               }
+               return null;
+       }
+       /**
+        * Get list of enabled location provider based on selected strategy
+        * 
+        * @param       strategy: provider location strategy
+        * @return      List of enabled provider based on selected strategy
+        */
+       private List<String> getLocationProviderList(int strategy)
+       {
+               List<String> providerList = new ArrayList<String>(0);
+               providerList.clear();
+               switch (strategy)
+               {
+                       case STRATEGY_NET_ONLY:
+                               if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
+                                       providerList.add(LocationManager.NETWORK_PROVIDER);
+                               break;
+                       case STRATEGY_GPS_ONLY:
+                               if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
+                                       providerList.add(LocationManager.GPS_PROVIDER);
+                               break;
+                       case STRATEGY_NET_AND_GPS:
+                               if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
+                                       providerList.add(LocationManager.NETWORK_PROVIDER);
+                               if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
+                                       providerList.add(LocationManager.GPS_PROVIDER);
+                               break;
+               }
+               return providerList;
+       }
+
+       /**
+        * Internal handler to get new location based on location strategy set
+        */
+       private final Runnable locationTask = new Runnable()
+       {
+               @Override
+               public void run()
+               {
+                       locationHandler.removeCallbacks(locationTask);
+                       if (AgentConnectorService.gettingNewLocation)
+                       {
+                               AgentConnectorService.gettingNewLocation = false;
+                               locationManager.removeUpdates(AgentConnectorService.this);
+                               locationHandler.postDelayed(locationTask, getNextSchedule(locationInterval));
+                               String locStatus = getString(R.string.info_location_timeout, allowedProviders);
+                               Log.i(TAG, locStatus);
+                               updateLocationStatus(locStatus);
+                               refreshHomeScreen();
+                       }
+                       else if (locationForce)
+                       {
+                               locationProvider = "";
+                               String locStatus = getString(R.string.info_location_no_provider);
+                               List<String> providerList = getLocationProviderList(locationStrategy);
+                               if (providerList.size() > 0)
+                               {
+                                       allowedProviders = "";
+                                       for (int i = 0; i < providerList.size(); i++)
+                                       {
+                                               allowedProviders += (i > 0 ? ", " : "") + providerList.get(i);
+                                               locationManager.requestLocationUpdates(providerList.get(i), 0, 0, AgentConnectorService.this); // 0, 0 to have it ASAP
+                                       }
+                                       AgentConnectorService.gettingNewLocation = true;
+                                       locationHandler.postDelayed(locationTask, getNextSchedule(locationDuration));
+                                       locStatus = getString(R.string.info_location_acquiring, allowedProviders);
+                               }
+                               else
+                                       locationHandler.postDelayed(locationTask, getNextSchedule(locationInterval));
+                               Log.i(TAG, locStatus);
+                               updateLocationStatus(locStatus);
+                               refreshHomeScreen();
+                       }
+                       else
+                               locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, AgentConnectorService.this); // 0, 0 to have it ASAP
+               }
+       };
+
+       @Override
+       public void onLocationChanged(Location location)
+       {
+               String locStatus = getString(R.string.info_location_good,
+                               TimeHelper.getTimeString(location.getTime()),
+                               locationProvider = location.getProvider(),
+                               Float.toString((float)location.getLatitude()),
+                               Float.toString((float)location.getLongitude()),
+                               Float.toString(location.getAccuracy()));
+               Log.i(TAG, locStatus);
+               updateLocationStatus(locStatus);
+               locationManager.removeUpdates(AgentConnectorService.this);
+               gettingNewLocation = false;
+               locationHandler.removeCallbacks(locationTask);
+               locationHandler.postDelayed(locationTask, locationInterval);
+               refreshHomeScreen();
+       }
+
+       @Override
+       public void onProviderDisabled(String provider)
+       {
+               Log.d(TAG, "onProviderDisabled: " + provider);
+       }
+
+       @Override
+       public void onProviderEnabled(String provider)
+       {
+               Log.d(TAG, "onProviderEnabled: " + provider);
+       }
+
+       @Override
+       public void onStatusChanged(String provider, int status, Bundle extras)
+       {
+               Log.d(TAG, "onStatusChanged: " + provider + " status: " + status);
+       }
+}
index b9c104a..a5e211f 100644 (file)
@@ -9,8 +9,9 @@
        <classpathentry kind="lib" path="libs/acra-4.4.0.jar"/>
        <classpathentry kind="src" path="src"/>
        <classpathentry kind="src" path="gen"/>
-       <classpathentry kind="lib" path="libs/netxms-base-1.2.9.jar"/>
-       <classpathentry kind="lib" path="libs/netxms-client-1.2.9.jar"/>
-       <classpathentry kind="lib" path="libs/netxms-client-api-1.2.9.jar"/>
+       <classpathentry kind="lib" path="libs/certificate-manager-1.2.10.jar"/>
+       <classpathentry kind="lib" path="libs/netxms-base-1.2.10.jar"/>
+       <classpathentry kind="lib" path="libs/netxms-client-1.2.10.jar"/>
+       <classpathentry kind="lib" path="libs/netxms-client-api-1.2.10.jar"/>
        <classpathentry kind="output" path="bin/classes"/>
 </classpath>
diff --git a/android/src/console/.externalToolBuilders/Generate build_number.xml from SVN revision.launch b/android/src/console/.externalToolBuilders/Generate build_number.xml from SVN revision.launch
deleted file mode 100644 (file)
index 3d7ee31..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
-<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>
-<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/android-console"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.debug.core.capture_output" value="false"/>
-<booleanAttribute key="org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON" value="false"/>
-<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="android-console"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/android-console/.externalToolBuilders/generate_build_number.xml}"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>
-<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
-<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/android-console/.externalToolBuilders}"/>
-</launchConfiguration>
diff --git a/android/src/console/.externalToolBuilders/build_number.xml b/android/src/console/.externalToolBuilders/build_number.xml
deleted file mode 100644 (file)
index b8a19a9..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-       <string name="build_number">@revision@</string>
-</resources>
diff --git a/android/src/console/.externalToolBuilders/generate_build.pl b/android/src/console/.externalToolBuilders/generate_build.pl
deleted file mode 100755 (executable)
index 833b786..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/perl
-
-$version = `svnversion`;
-chomp $version;
-if ($version =~ /[0-9]+\:([0-9]+)/)
-{
-       $version = $1;
-}
-print "Setting build number to $version\n";
-
-open(OUT, ">build_number.xml") || die "out: $!";
-
-print OUT "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
-print OUT "<resources>\n";
-print OUT "\t<string name=\"build_number\">$version</string>\n";
-print OUT "</resources>\n";
-
-close OUT;
-exit 0;
diff --git a/android/src/console/.externalToolBuilders/generate_build_number.xml b/android/src/console/.externalToolBuilders/generate_build_number.xml
deleted file mode 100644 (file)
index 9174e31..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<project name="Build Script" default="make" basedir=".">
-    <target name="make">
-        <!-- Get current working directory. -->
-        <exec executable="pwd" outputproperty="dir.root" />
-        <!-- Get subversion revision number. -->
-        <exec executable="svnversion" outputproperty="repository.revision" />
-        <echo message="Repository revision is ${repository.revision}" />
-
-               <copy overwrite="true" file="build_number.xml" toFile="../res/values/build_number.xml">
-                       <filterset>
-                               <filter token="revision" value="${repository.revision}" />
-                       </filterset>
-               </copy>
-    </target>
-</project>
diff --git a/android/src/console/.externalToolBuilders/run.exe b/android/src/console/.externalToolBuilders/run.exe
deleted file mode 100644 (file)
index d065ee0..0000000
Binary files a/android/src/console/.externalToolBuilders/run.exe and /dev/null differ
index 795f928..659be1b 100644 (file)
@@ -5,16 +5,6 @@
        <projects>
        </projects>
        <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
-                       <triggers>full,incremental,</triggers>
-                       <arguments>
-                               <dictionary>
-                                       <key>LaunchConfigHandle</key>
-                                       <value>&lt;project&gt;/.externalToolBuilders/Generate build_number.xml from SVN revision.launch</value>
-                               </dictionary>
-                       </arguments>
-               </buildCommand>
                <buildCommand>
                        <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
                        <arguments>
index 540a2bd..fc93a1a 100644 (file)
-package org.netxms.ui.android.loaders;\r
-\r
-import java.util.Map;\r
-\r
-import org.netxms.client.events.Alarm;\r
-import org.netxms.ui.android.service.ClientConnectorService;\r
-\r
-import android.content.Context;\r
-import android.support.v4.content.AsyncTaskLoader;\r
-import android.util.Log;\r
-\r
-/**\r
- * Background loader for Alarm objects. Notifies the fragment on job complete\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- *\r
- */\r
-\r
-public class AlarmLoader extends AsyncTaskLoader<Alarm[]>\r
-{\r
-       private static final String TAG = "nxclient/AlarmLoader";\r
-\r
-       private ClientConnectorService service = null;\r
-       private Alarm[] alarms = null;\r
-\r
-       public AlarmLoader(Context context)\r
-       {\r
-               // Loaders may be used across multiple Activitys (assuming they aren't\r
-               // bound to the LoaderManager), so NEVER hold a reference to the context\r
-               // directly. Doing so will cause you to leak an entire Activity's context.\r
-               // The superclass constructor will store a reference to the Application\r
-               // Context instead, and can be retrieved with a call to getContext().\r
-               super(context);\r
-       }\r
-\r
-       public void setService(ClientConnectorService service)\r
-       {\r
-               this.service = service;\r
-               if (service != null)\r
-                       service.registerAlarmLoader(this);\r
-       }\r
-\r
-       @Override\r
-       public Alarm[] loadInBackground()\r
-       {\r
-               try\r
-               {\r
-                       alarms = null;\r
-                       if (service != null && service.getSession() != null)\r
-                       {\r
-                               Map<Long, Alarm> list = service.getSession().getAlarms();\r
-                               if (list != null)\r
-                                       alarms = list.values().toArray(new Alarm[list.size()]);\r
-                       }\r
-                       else\r
-                               Log.d(TAG, "loadInBackground: service or session null!");\r
-               }\r
-               catch (Exception e)\r
-               {\r
-                       Log.e(TAG, "Exception while executing loadInBackground", e);\r
-               }\r
-               return alarms;\r
-       }\r
-\r
-       @Override\r
-       public void deliverResult(Alarm[] values)\r
-       {\r
-               if (isReset())\r
-               {\r
-                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");\r
-                       // The Loader has been reset; ignore the result and invalidate the data.\r
-                       // This can happen when the Loader is reset while an asynchronous query\r
-                       // is working in the background. That is, when the background thread\r
-                       // finishes its work and attempts to deliver the results to the client,\r
-                       // it will see here that the Loader has been reset and discard any\r
-                       // resources associated with the new data as necessary.\r
-                       if (values != null)\r
-                       {\r
-                               onReleaseResources(values);\r
-                               return;\r
-                       }\r
-               }\r
-               // Hold a reference to the old data so it doesn't get garbage collected.\r
-               // The old data may still be in use (i.e. bound to an adapter, etc.), so\r
-               // we must protect it until the new data has been delivered.\r
-               Alarm[] oldValues = alarms;\r
-               alarms = values;\r
-\r
-               if (isStarted())\r
-               {\r
-                       // If the Loader is in a started state, have the superclass deliver the\r
-                       // results to the client.\r
-                       super.deliverResult(values);\r
-               }\r
-\r
-               // Invalidate the old data as we don't need it any more.\r
-               if (oldValues != null && oldValues != values)\r
-               {\r
-                       onReleaseResources(oldValues);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStartLoading()\r
-       {\r
-               if (alarms != null)\r
-               {\r
-                       // Deliver any previously loaded data immediately.\r
-                       Log.i(TAG, "Delivering previously loaded data to the client...");\r
-                       deliverResult(alarms);\r
-               }\r
-\r
-               // Register the observers that will notify the Loader when changes are made.\r
-               if (service != null)\r
-                       service.registerAlarmLoader(this);\r
-\r
-               if (takeContentChanged())\r
-               {\r
-                       // When the observer detects a new installed application, it will call\r
-                       // onContentChanged() on the Loader, which will cause the next call to\r
-                       // takeContentChanged() to return true. If this is ever the case (or if\r
-                       // the current data is null), we force a new load.\r
-                       Log.i(TAG, "A content change has been detected... so force load!");\r
-                       forceLoad();\r
-               }\r
-               else if (alarms == null)\r
-               {\r
-                       // If the current data is null... then we should make it non-null! :)\r
-                       Log.i(TAG, "The current data is null... so force load!");\r
-                       forceLoad();\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStopLoading()\r
-       {\r
-               // The Loader has been put in a stopped state, so we should attempt to\r
-               // cancel the current load (if there is one).\r
-               cancelLoad();\r
-\r
-               // Note that we leave the observer as is; Loaders in a stopped state\r
-               // should still monitor the data source for changes so that the Loader\r
-               // will know to force a new load if it is ever started again.\r
-       }\r
-\r
-       @Override\r
-       protected void onReset()\r
-       {\r
-               // Ensure the loader is stopped.\r
-               onStopLoading();\r
-\r
-               // At this point we can release the resources associated with 'apps'.\r
-               if (alarms != null)\r
-               {\r
-                       onReleaseResources(alarms);\r
-                       alarms = null;\r
-               }\r
-\r
-               // The Loader is being reset, so we should stop monitoring for changes.\r
-               if (service != null)\r
-                       service.unregisterAlarmLoader(this);\r
-       }\r
-\r
-       @Override\r
-       public void onCanceled(Alarm[] values)\r
-       {\r
-               // Attempt to cancel the current asynchronous load.\r
-               super.onCanceled(values);\r
-\r
-               // The load has been canceled, so we should release the resources\r
-               // associated with 'alarms'.\r
-               onReleaseResources(values);\r
-       }\r
-\r
-       @Override\r
-       public void forceLoad()\r
-       {\r
-               super.forceLoad();\r
-       }\r
-\r
-       /**\r
-        * Helper method to take care of releasing resources associated with an\r
-        * actively loaded data set.\r
-        */\r
-       protected void onReleaseResources(Alarm[] values)\r
-       {\r
-               // For a simple List, there is nothing to do. For something like a Cursor,\r
-               // we would close it in this method. All resources associated with the\r
-               // Loader should be released here.\r
-       }\r
-}\r
+package org.netxms.ui.android.loaders;
+
+import java.util.Map;
+
+import org.netxms.client.events.Alarm;
+import org.netxms.ui.android.service.ClientConnectorService;
+
+import android.content.Context;
+import android.support.v4.content.AsyncTaskLoader;
+import android.util.Log;
+
+/**
+ * Background loader for Alarm objects. Notifies the fragment on job complete
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ *
+ */
+
+public class AlarmLoader extends AsyncTaskLoader<Alarm[]>
+{
+       private static final String TAG = "nxclient/AlarmLoader";
+
+       private ClientConnectorService service = null;
+       private Alarm[] alarms = null;
+
+       public AlarmLoader(Context context)
+       {
+               // Loaders may be used across multiple Activitys (assuming they aren't
+               // bound to the LoaderManager), so NEVER hold a reference to the context
+               // directly. Doing so will cause you to leak an entire Activity's context.
+               // The superclass constructor will store a reference to the Application
+               // Context instead, and can be retrieved with a call to getContext().
+               super(context);
+       }
+
+       public void setService(ClientConnectorService service)
+       {
+               this.service = service;
+               if (service != null)
+                       service.registerAlarmLoader(this);
+       }
+
+       @Override
+       public Alarm[] loadInBackground()
+       {
+               try
+               {
+                       alarms = null;
+                       if (service != null && service.getSession() != null)
+                       {
+                               Map<Long, Alarm> list = service.getSession().getAlarms();
+                               if (list != null)
+                                       alarms = list.values().toArray(new Alarm[list.size()]);
+                       }
+                       else
+                               Log.d(TAG, "loadInBackground: service or session null!");
+               }
+               catch (Exception e)
+               {
+                       Log.e(TAG, "Exception while executing loadInBackground", e);
+               }
+               return alarms;
+       }
+
+       @Override
+       public void deliverResult(Alarm[] values)
+       {
+               if (isReset())
+               {
+                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");
+                       // The Loader has been reset; ignore the result and invalidate the data.
+                       // This can happen when the Loader is reset while an asynchronous query
+                       // is working in the background. That is, when the background thread
+                       // finishes its work and attempts to deliver the results to the client,
+                       // it will see here that the Loader has been reset and discard any
+                       // resources associated with the new data as necessary.
+                       if (values != null)
+                       {
+                               onReleaseResources(values);
+                               return;
+                       }
+               }
+               // Hold a reference to the old data so it doesn't get garbage collected.
+               // The old data may still be in use (i.e. bound to an adapter, etc.), so
+               // we must protect it until the new data has been delivered.
+               Alarm[] oldValues = alarms;
+               alarms = values;
+
+               if (isStarted())
+               {
+                       // If the Loader is in a started state, have the superclass deliver the
+                       // results to the client.
+                       super.deliverResult(values);
+               }
+
+               // Invalidate the old data as we don't need it any more.
+               if (oldValues != null && oldValues != values)
+               {
+                       onReleaseResources(oldValues);
+               }
+       }
+
+       @Override
+       protected void onStartLoading()
+       {
+               if (alarms != null)
+               {
+                       // Deliver any previously loaded data immediately.
+                       Log.i(TAG, "Delivering previously loaded data to the client...");
+                       deliverResult(alarms);
+               }
+
+               // Register the observers that will notify the Loader when changes are made.
+               if (service != null)
+                       service.registerAlarmLoader(this);
+
+               if (takeContentChanged())
+               {
+                       // When the observer detects a new installed application, it will call
+                       // onContentChanged() on the Loader, which will cause the next call to
+                       // takeContentChanged() to return true. If this is ever the case (or if
+                       // the current data is null), we force a new load.
+                       Log.i(TAG, "A content change has been detected... so force load!");
+                       forceLoad();
+               }
+               else if (alarms == null)
+               {
+                       // If the current data is null... then we should make it non-null! :)
+                       Log.i(TAG, "The current data is null... so force load!");
+                       forceLoad();
+               }
+       }
+
+       @Override
+       protected void onStopLoading()
+       {
+               // The Loader has been put in a stopped state, so we should attempt to
+               // cancel the current load (if there is one).
+               cancelLoad();
+
+               // Note that we leave the observer as is; Loaders in a stopped state
+               // should still monitor the data source for changes so that the Loader
+               // will know to force a new load if it is ever started again.
+       }
+
+       @Override
+       protected void onReset()
+       {
+               // Ensure the loader is stopped.
+               onStopLoading();
+
+               // At this point we can release the resources associated with 'apps'.
+               if (alarms != null)
+               {
+                       onReleaseResources(alarms);
+                       alarms = null;
+               }
+
+               // The Loader is being reset, so we should stop monitoring for changes.
+               if (service != null)
+                       service.unregisterAlarmLoader(this);
+       }
+
+       @Override
+       public void onCanceled(Alarm[] values)
+       {
+               // Attempt to cancel the current asynchronous load.
+               super.onCanceled(values);
+
+               // The load has been canceled, so we should release the resources
+               // associated with 'alarms'.
+               onReleaseResources(values);
+       }
+
+       @Override
+       public void forceLoad()
+       {
+               super.forceLoad();
+       }
+
+       /**
+        * Helper method to take care of releasing resources associated with an
+        * actively loaded data set.
+        */
+       protected void onReleaseResources(Alarm[] values)
+       {
+               // For a simple List, there is nothing to do. For something like a Cursor,
+               // we would close it in this method. All resources associated with the
+               // Loader should be released here.
+       }
+}
index 1bca89f..a80244e 100644 (file)
-package org.netxms.ui.android.loaders;\r
-\r
-import org.netxms.client.NXCSession;\r
-import org.netxms.client.datacollection.DciValue;\r
-import org.netxms.ui.android.service.ClientConnectorService;\r
-\r
-import android.content.Context;\r
-import android.support.v4.content.AsyncTaskLoader;\r
-import android.util.Log;\r
-\r
-/**\r
- * Background loader for DciValue objects. Notifies the fragment on job complete\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- *\r
- */\r
-\r
-public class DciValueLoader extends AsyncTaskLoader<DciValue[]>\r
-{\r
-       private static final String TAG = "nxclient/DciValueLoader";\r
-\r
-       private ClientConnectorService service = null;\r
-       private long nodeId = 0;\r
-       private DciValue[] dciValues = null;\r
-\r
-       public DciValueLoader(Context context)\r
-       {\r
-               // Loaders may be used across multiple Activitys (assuming they aren't\r
-               // bound to the LoaderManager), so NEVER hold a reference to the context\r
-               // directly. Doing so will cause you to leak an entire Activity's context.\r
-               // The superclass constructor will store a reference to the Application\r
-               // Context instead, and can be retrieved with a call to getContext().\r
-               super(context);\r
-       }\r
-\r
-       public void setObjId(long id)\r
-       {\r
-               this.nodeId = id;\r
-       }\r
-\r
-       public void setService(ClientConnectorService service)\r
-       {\r
-               this.service = service;\r
-               if (service != null)\r
-                       service.registerDciValueLoader(this);\r
-       }\r
-\r
-       @Override\r
-       public DciValue[] loadInBackground()\r
-       {\r
-               try\r
-               {\r
-                       dciValues = null;\r
-                       if (service != null && service.getSession() != null)\r
-                       {\r
-                               service.getSession().syncObjectSet(new long[] { nodeId }, false, NXCSession.OBJECT_SYNC_WAIT);\r
-                               dciValues = service.getSession().getLastValues(nodeId);\r
-                       }\r
-                       else\r
-                               Log.d(TAG, "loadInBackground: service or session null!");\r
-               }\r
-               catch (Exception e)\r
-               {\r
-                       Log.e(TAG, "Exception while executing loadInBackground", e);\r
-               }\r
-               return dciValues;\r
-       }\r
-\r
-       @Override\r
-       public void deliverResult(DciValue[] values)\r
-       {\r
-               if (isReset())\r
-               {\r
-                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");\r
-                       // The Loader has been reset; ignore the result and invalidate the data.\r
-                       // This can happen when the Loader is reset while an asynchronous query\r
-                       // is working in the background. That is, when the background thread\r
-                       // finishes its work and attempts to deliver the results to the client,\r
-                       // it will see here that the Loader has been reset and discard any\r
-                       // resources associated with the new data as necessary.\r
-                       if (values != null)\r
-                       {\r
-                               onReleaseResources(values);\r
-                               return;\r
-                       }\r
-               }\r
-               // Hold a reference to the old data so it doesn't get garbage collected.\r
-               // The old data may still be in use (i.e. bound to an adapter, etc.), so\r
-               // we must protect it until the new data has been delivered.\r
-               DciValue[] oldValues = dciValues;\r
-               dciValues = values;\r
-\r
-               if (isStarted())\r
-               {\r
-                       // If the Loader is in a started state, have the superclass deliver the\r
-                       // results to the client.\r
-                       super.deliverResult(values);\r
-               }\r
-\r
-               // Invalidate the old data as we don't need it any more.\r
-               if (oldValues != null && oldValues != values)\r
-               {\r
-                       Log.i(TAG, "Releasing any old data associated with this Loader.");\r
-                       onReleaseResources(oldValues);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStartLoading()\r
-       {\r
-\r
-               if (dciValues != null)\r
-               {\r
-                       // Deliver any previously loaded data immediately.\r
-                       Log.i(TAG, "Delivering previously loaded data to the client...");\r
-                       deliverResult(dciValues);\r
-               }\r
-\r
-               // Register the observers that will notify the Loader when changes are made.\r
-               if (service != null)\r
-                       service.registerDciValueLoader(this);\r
-\r
-               if (takeContentChanged())\r
-               {\r
-                       // When the observer detects a new installed application, it will call\r
-                       // onContentChanged() on the Loader, which will cause the next call to\r
-                       // takeContentChanged() to return true. If this is ever the case (or if\r
-                       // the current data is null), we force a new load.\r
-                       Log.i(TAG, "A content change has been detected... so force load!");\r
-                       forceLoad();\r
-               }\r
-               else if (dciValues == null)\r
-               {\r
-                       // If the current data is null... then we should make it non-null! :)\r
-                       Log.i(TAG, "The current data is null... so force load!");\r
-                       forceLoad();\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStopLoading()\r
-       {\r
-               // The Loader has been put in a stopped state, so we should attempt to\r
-               // cancel the current load (if there is one).\r
-               cancelLoad();\r
-\r
-               // Note that we leave the observer as is; Loaders in a stopped state\r
-               // should still monitor the data source for changes so that the Loader\r
-               // will know to force a new load if it is ever started again.\r
-       }\r
-\r
-       @Override\r
-       protected void onReset()\r
-       {\r
-               // Ensure the loader is stopped.\r
-               onStopLoading();\r
-\r
-               // At this point we can release the resources associated with 'apps'.\r
-               if (dciValues != null)\r
-               {\r
-                       onReleaseResources(dciValues);\r
-                       dciValues = null;\r
-               }\r
-\r
-               // The Loader is being reset, so we should stop monitoring for changes.\r
-               if (service != null)\r
-                       service.unregisterDciValueLoader(this);\r
-       }\r
-\r
-       @Override\r
-       public void onCanceled(DciValue[] values)\r
-       {\r
-               // Attempt to cancel the current asynchronous load.\r
-               super.onCanceled(values);\r
-\r
-               // The load has been canceled, so we should release the resources\r
-               // associated with 'dciValues'.\r
-               onReleaseResources(values);\r
-       }\r
-\r
-       @Override\r
-       public void forceLoad()\r
-       {\r
-               super.forceLoad();\r
-       }\r
-\r
-       /**\r
-        * Helper method to take care of releasing resources associated with an\r
-        * actively loaded data set.\r
-        */\r
-       protected void onReleaseResources(DciValue[] values)\r
-       {\r
-               // For a simple List, there is nothing to do. For something like a Cursor,\r
-               // we would close it in this method. All resources associated with the\r
-               // Loader should be released here.\r
-       }\r
-}\r
+package org.netxms.ui.android.loaders;
+
+import org.netxms.client.NXCSession;
+import org.netxms.client.datacollection.DciValue;
+import org.netxms.ui.android.service.ClientConnectorService;
+
+import android.content.Context;
+import android.support.v4.content.AsyncTaskLoader;
+import android.util.Log;
+
+/**
+ * Background loader for DciValue objects. Notifies the fragment on job complete
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ *
+ */
+
+public class DciValueLoader extends AsyncTaskLoader<DciValue[]>
+{
+       private static final String TAG = "nxclient/DciValueLoader";
+
+       private ClientConnectorService service = null;
+       private long nodeId = 0;
+       private DciValue[] dciValues = null;
+
+       public DciValueLoader(Context context)
+       {
+               // Loaders may be used across multiple Activitys (assuming they aren't
+               // bound to the LoaderManager), so NEVER hold a reference to the context
+               // directly. Doing so will cause you to leak an entire Activity's context.
+               // The superclass constructor will store a reference to the Application
+               // Context instead, and can be retrieved with a call to getContext().
+               super(context);
+       }
+
+       public void setObjId(long id)
+       {
+               this.nodeId = id;
+       }
+
+       public void setService(ClientConnectorService service)
+       {
+               this.service = service;
+               if (service != null)
+                       service.registerDciValueLoader(this);
+       }
+
+       @Override
+       public DciValue[] loadInBackground()
+       {
+               try
+               {
+                       dciValues = null;
+                       if (service != null && service.getSession() != null)
+                       {
+                               service.getSession().syncObjectSet(new long[] { nodeId }, false, NXCSession.OBJECT_SYNC_WAIT);
+                               dciValues = service.getSession().getLastValues(nodeId);
+                       }
+                       else
+                               Log.d(TAG, "loadInBackground: service or session null!");
+               }
+               catch (Exception e)
+               {
+                       Log.e(TAG, "Exception while executing loadInBackground", e);
+               }
+               return dciValues;
+       }
+
+       @Override
+       public void deliverResult(DciValue[] values)
+       {
+               if (isReset())
+               {
+                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");
+                       // The Loader has been reset; ignore the result and invalidate the data.
+                       // This can happen when the Loader is reset while an asynchronous query
+                       // is working in the background. That is, when the background thread
+                       // finishes its work and attempts to deliver the results to the client,
+                       // it will see here that the Loader has been reset and discard any
+                       // resources associated with the new data as necessary.
+                       if (values != null)
+                       {
+                               onReleaseResources(values);
+                               return;
+                       }
+               }
+               // Hold a reference to the old data so it doesn't get garbage collected.
+               // The old data may still be in use (i.e. bound to an adapter, etc.), so
+               // we must protect it until the new data has been delivered.
+               DciValue[] oldValues = dciValues;
+               dciValues = values;
+
+               if (isStarted())
+               {
+                       // If the Loader is in a started state, have the superclass deliver the
+                       // results to the client.
+                       super.deliverResult(values);
+               }
+
+               // Invalidate the old data as we don't need it any more.
+               if (oldValues != null && oldValues != values)
+               {
+                       Log.i(TAG, "Releasing any old data associated with this Loader.");
+                       onReleaseResources(oldValues);
+               }
+       }
+
+       @Override
+       protected void onStartLoading()
+       {
+
+               if (dciValues != null)
+               {
+                       // Deliver any previously loaded data immediately.
+                       Log.i(TAG, "Delivering previously loaded data to the client...");
+                       deliverResult(dciValues);
+               }
+
+               // Register the observers that will notify the Loader when changes are made.
+               if (service != null)
+                       service.registerDciValueLoader(this);
+
+               if (takeContentChanged())
+               {
+                       // When the observer detects a new installed application, it will call
+                       // onContentChanged() on the Loader, which will cause the next call to
+                       // takeContentChanged() to return true. If this is ever the case (or if
+                       // the current data is null), we force a new load.
+                       Log.i(TAG, "A content change has been detected... so force load!");
+                       forceLoad();
+               }
+               else if (dciValues == null)
+               {
+                       // If the current data is null... then we should make it non-null! :)
+                       Log.i(TAG, "The current data is null... so force load!");
+                       forceLoad();
+               }
+       }
+
+       @Override
+       protected void onStopLoading()
+       {
+               // The Loader has been put in a stopped state, so we should attempt to
+               // cancel the current load (if there is one).
+               cancelLoad();
+
+               // Note that we leave the observer as is; Loaders in a stopped state
+               // should still monitor the data source for changes so that the Loader
+               // will know to force a new load if it is ever started again.
+       }
+
+       @Override
+       protected void onReset()
+       {
+               // Ensure the loader is stopped.
+               onStopLoading();
+
+               // At this point we can release the resources associated with 'apps'.
+               if (dciValues != null)
+               {
+                       onReleaseResources(dciValues);
+                       dciValues = null;
+               }
+
+               // The Loader is being reset, so we should stop monitoring for changes.
+               if (service != null)
+                       service.unregisterDciValueLoader(this);
+       }
+
+       @Override
+       public void onCanceled(DciValue[] values)
+       {
+               // Attempt to cancel the current asynchronous load.
+               super.onCanceled(values);
+
+               // The load has been canceled, so we should release the resources
+               // associated with 'dciValues'.
+               onReleaseResources(values);
+       }
+
+       @Override
+       public void forceLoad()
+       {
+               super.forceLoad();
+       }
+
+       /**
+        * Helper method to take care of releasing resources associated with an
+        * actively loaded data set.
+        */
+       protected void onReleaseResources(DciValue[] values)
+       {
+               // For a simple List, there is nothing to do. For something like a Cursor,
+               // we would close it in this method. All resources associated with the
+               // Loader should be released here.
+       }
+}
index 52070bf..9a66cdf 100644 (file)
-package org.netxms.ui.android.loaders;\r
-\r
-import java.util.Set;\r
-\r
-import org.netxms.client.NXCSession;\r
-import org.netxms.client.objects.AbstractObject;\r
-import org.netxms.ui.android.service.ClientConnectorService;\r
-\r
-import android.content.Context;\r
-import android.support.v4.content.AsyncTaskLoader;\r
-import android.util.Log;\r
-\r
-/**\r
- * Background loader for a set of GenericObjects, such as list of interfaces.\r
- * Notifies the fragment on job complete\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- *\r
- */\r
-\r
-public class GenericObjectChildrenLoader extends AsyncTaskLoader<Set<AbstractObject>>\r
-{\r
-       private static final String TAG = "nxclient/GenericObjectChildrenLoader";\r
-\r
-       private ClientConnectorService service = null;\r
-       private long objId = 0;\r
-       private int classFilter = AbstractObject.OBJECT_INTERFACE;\r
-       private Set<AbstractObject> children = null;\r
-\r
-       public GenericObjectChildrenLoader(Context context)\r
-       {\r
-               // Loaders may be used across multiple Activitys (assuming they aren't\r
-               // bound to the LoaderManager), so NEVER hold a reference to the context\r
-               // directly. Doing so will cause you to leak an entire Activity's context.\r
-               // The superclass constructor will store a reference to the Application\r
-               // Context instead, and can be retrieved with a call to getContext().\r
-               super(context);\r
-       }\r
-\r
-       public void setObjId(long id)\r
-       {\r
-               this.objId = id;\r
-       }\r
-\r
-       public void setClassFilter(int classFilter)\r
-       {\r
-               this.classFilter = classFilter;\r
-       }\r
-\r
-       public void setService(ClientConnectorService service)\r
-       {\r
-               this.service = service;\r
-               if (service != null)\r
-                       service.registerGenericObjectChildrenLoader(this);\r
-       }\r
-\r
-       @Override\r
-       public Set<AbstractObject> loadInBackground()\r
-       {\r
-               try\r
-               {\r
-                       children = null;\r
-                       if (service != null && service.getSession() != null)\r
-                       {\r
-                               service.getSession().syncObjectSet(new long[] { objId }, false, NXCSession.OBJECT_SYNC_WAIT);\r
-                               AbstractObject go = service.getSession().findObjectById(objId);\r
-                               if (go != null)\r
-                               {\r
-                                       service.getSession().syncMissingObjects(go.getChildIdList(), false, NXCSession.OBJECT_SYNC_WAIT);\r
-                                       children = go.getAllChilds(classFilter);\r
-                               }\r
-                       }\r
-                       else\r
-                               Log.d(TAG, "loadInBackground: service or session null!");\r
-               }\r
-               catch (Exception e)\r
-               {\r
-                       Log.e(TAG, "Exception while executing loadInBackground", e);\r
-               }\r
-               return children;\r
-       }\r
-\r
-       @Override\r
-       public void deliverResult(Set<AbstractObject> newChildren)\r
-       {\r
-               if (isReset())\r
-               {\r
-                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");\r
-                       // The Loader has been reset; ignore the result and invalidate the data.\r
-                       // This can happen when the Loader is reset while an asynchronous query\r
-                       // is working in the background. That is, when the background thread\r
-                       // finishes its work and attempts to deliver the results to the client,\r
-                       // it will see here that the Loader has been reset and discard any\r
-                       // resources associated with the new data as necessary.\r
-                       if (newChildren != null)\r
-                       {\r
-                               onReleaseResources(newChildren);\r
-                               return;\r
-                       }\r
-               }\r
-               // Hold a reference to the old data so it doesn't get garbage collected.\r
-               // The old data may still be in use (i.e. bound to an adapter, etc.), so\r
-               // we must protect it until the new data has been delivered.\r
-               Set<AbstractObject> oldChildren = children;\r
-               children = newChildren;\r
-\r
-               if (isStarted())\r
-               {\r
-                       // If the Loader is in a started state, have the superclass deliver the\r
-                       // results to the client.\r
-                       super.deliverResult(newChildren);\r
-               }\r
-\r
-               // Invalidate the old data as we don't need it any more.\r
-               if (oldChildren != null && oldChildren != newChildren)\r
-               {\r
-                       Log.i(TAG, "Releasing any old data associated with this Loader.");\r
-                       onReleaseResources(oldChildren);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStartLoading()\r
-       {\r
-               if (children != null)\r
-               {\r
-                       // Deliver any previously loaded data immediately.\r
-                       Log.i(TAG, "Delivering previously loaded data to the client...");\r
-                       deliverResult(children);\r
-               }\r
-\r
-               // Register the observers that will notify the Loader when changes are made.\r
-               if (service != null)\r
-                       service.registerGenericObjectChildrenLoader(this);\r
-\r
-               if (takeContentChanged())\r
-               {\r
-                       // When the observer detects a new installed application, it will call\r
-                       // onContentChanged() on the Loader, which will cause the next call to\r
-                       // takeContentChanged() to return true. If this is ever the case (or if\r
-                       // the current data is null), we force a new load.\r
-                       Log.i(TAG, "A content change has been detected... so force load!");\r
-                       forceLoad();\r
-               }\r
-               else if (children == null)\r
-               {\r
-                       // If the current data is null... then we should make it non-null! :)\r
-                       Log.i(TAG, "The current data is null... so force load!");\r
-                       forceLoad();\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStopLoading()\r
-       {\r
-               // The Loader has been put in a stopped state, so we should attempt to\r
-               // cancel the current load (if there is one).\r
-               cancelLoad();\r
-\r
-               // Note that we leave the observer as is; Loaders in a stopped state\r
-               // should still monitor the data source for changes so that the Loader\r
-               // will know to force a new load if it is ever started again.\r
-       }\r
-\r
-       @Override\r
-       protected void onReset()\r
-       {\r
-               // Ensure the loader is stopped.\r
-               onStopLoading();\r
-\r
-               // At this point we can release the resources associated with 'apps'.\r
-               if (children != null)\r
-               {\r
-                       onReleaseResources(children);\r
-                       children = null;\r
-               }\r
-\r
-               // The Loader is being reset, so we should stop monitoring for changes.\r
-               if (service != null)\r
-                       service.unregisterGenericObjectChildrenLoader(this);\r
-       }\r
-\r
-       @Override\r
-       public void onCanceled(Set<AbstractObject> o)\r
-       {\r
-               // Attempt to cancel the current asynchronous load.\r
-               super.onCanceled(o);\r
-\r
-               // The load has been canceled, so we should release the resources\r
-               // associated with 'obj'.\r
-               onReleaseResources(o);\r
-       }\r
-\r
-       @Override\r
-       public void forceLoad()\r
-       {\r
-               super.forceLoad();\r
-       }\r
-\r
-       /**\r
-        * Helper method to take care of releasing resources associated with an\r
-        * actively loaded data set.\r
-        */\r
-       protected void onReleaseResources(Set<AbstractObject> o)\r
-       {\r
-               // For a simple List, there is nothing to do. For something like a Cursor,\r
-               // we would close it in this method. All resources associated with the\r
-               // Loader should be released here.\r
-       }\r
-}\r
+package org.netxms.ui.android.loaders;
+
+import java.util.Set;
+
+import org.netxms.client.NXCSession;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.android.service.ClientConnectorService;
+
+import android.content.Context;
+import android.support.v4.content.AsyncTaskLoader;
+import android.util.Log;
+
+/**
+ * Background loader for a set of GenericObjects, such as list of interfaces.
+ * Notifies the fragment on job complete
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ *
+ */
+
+public class GenericObjectChildrenLoader extends AsyncTaskLoader<Set<AbstractObject>>
+{
+       private static final String TAG = "nxclient/GenericObjectChildrenLoader";
+
+       private ClientConnectorService service = null;
+       private long objId = 0;
+       private int classFilter = AbstractObject.OBJECT_INTERFACE;
+       private Set<AbstractObject> children = null;
+
+       public GenericObjectChildrenLoader(Context context)
+       {
+               // Loaders may be used across multiple Activitys (assuming they aren't
+               // bound to the LoaderManager), so NEVER hold a reference to the context
+               // directly. Doing so will cause you to leak an entire Activity's context.
+               // The superclass constructor will store a reference to the Application
+               // Context instead, and can be retrieved with a call to getContext().
+               super(context);
+       }
+
+       public void setObjId(long id)
+       {
+               this.objId = id;
+       }
+
+       public void setClassFilter(int classFilter)
+       {
+               this.classFilter = classFilter;
+       }
+
+       public void setService(ClientConnectorService service)
+       {
+               this.service = service;
+               if (service != null)
+                       service.registerGenericObjectChildrenLoader(this);
+       }
+
+       @Override
+       public Set<AbstractObject> loadInBackground()
+       {
+               try
+               {
+                       children = null;
+                       if (service != null && service.getSession() != null)
+                       {
+                               service.getSession().syncObjectSet(new long[] { objId }, false, NXCSession.OBJECT_SYNC_WAIT);
+                               AbstractObject go = service.getSession().findObjectById(objId);
+                               if (go != null)
+                               {
+                                       service.getSession().syncMissingObjects(go.getChildIdList(), false, NXCSession.OBJECT_SYNC_WAIT);
+                                       children = go.getAllChilds(classFilter);
+                               }
+                       }
+                       else
+                               Log.d(TAG, "loadInBackground: service or session null!");
+               }
+               catch (Exception e)
+               {
+                       Log.e(TAG, "Exception while executing loadInBackground", e);
+               }
+               return children;
+       }
+
+       @Override
+       public void deliverResult(Set<AbstractObject> newChildren)
+       {
+               if (isReset())
+               {
+                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");
+                       // The Loader has been reset; ignore the result and invalidate the data.
+                       // This can happen when the Loader is reset while an asynchronous query
+                       // is working in the background. That is, when the background thread
+                       // finishes its work and attempts to deliver the results to the client,
+                       // it will see here that the Loader has been reset and discard any
+                       // resources associated with the new data as necessary.
+                       if (newChildren != null)
+                       {
+                               onReleaseResources(newChildren);
+                               return;
+                       }
+               }
+               // Hold a reference to the old data so it doesn't get garbage collected.
+               // The old data may still be in use (i.e. bound to an adapter, etc.), so
+               // we must protect it until the new data has been delivered.
+               Set<AbstractObject> oldChildren = children;
+               children = newChildren;
+
+               if (isStarted())
+               {
+                       // If the Loader is in a started state, have the superclass deliver the
+                       // results to the client.
+                       super.deliverResult(newChildren);
+               }
+
+               // Invalidate the old data as we don't need it any more.
+               if (oldChildren != null && oldChildren != newChildren)
+               {
+                       Log.i(TAG, "Releasing any old data associated with this Loader.");
+                       onReleaseResources(oldChildren);
+               }
+       }
+
+       @Override
+       protected void onStartLoading()
+       {
+               if (children != null)
+               {
+                       // Deliver any previously loaded data immediately.
+                       Log.i(TAG, "Delivering previously loaded data to the client...");
+                       deliverResult(children);
+               }
+
+               // Register the observers that will notify the Loader when changes are made.
+               if (service != null)
+                       service.registerGenericObjectChildrenLoader(this);
+
+               if (takeContentChanged())
+               {
+                       // When the observer detects a new installed application, it will call
+                       // onContentChanged() on the Loader, which will cause the next call to
+                       // takeContentChanged() to return true. If this is ever the case (or if
+                       // the current data is null), we force a new load.
+                       Log.i(TAG, "A content change has been detected... so force load!");
+                       forceLoad();
+               }
+               else if (children == null)
+               {
+                       // If the current data is null... then we should make it non-null! :)
+                       Log.i(TAG, "The current data is null... so force load!");
+                       forceLoad();
+               }
+       }
+
+       @Override
+       protected void onStopLoading()
+       {
+               // The Loader has been put in a stopped state, so we should attempt to
+               // cancel the current load (if there is one).
+               cancelLoad();
+
+               // Note that we leave the observer as is; Loaders in a stopped state
+               // should still monitor the data source for changes so that the Loader
+               // will know to force a new load if it is ever started again.
+       }
+
+       @Override
+       protected void onReset()
+       {
+               // Ensure the loader is stopped.
+               onStopLoading();
+
+               // At this point we can release the resources associated with 'apps'.
+               if (children != null)
+               {
+                       onReleaseResources(children);
+                       children = null;
+               }
+
+               // The Loader is being reset, so we should stop monitoring for changes.
+               if (service != null)
+                       service.unregisterGenericObjectChildrenLoader(this);
+       }
+
+       @Override
+       public void onCanceled(Set<AbstractObject> o)
+       {
+               // Attempt to cancel the current asynchronous load.
+               super.onCanceled(o);
+
+               // The load has been canceled, so we should release the resources
+               // associated with 'obj'.
+               onReleaseResources(o);
+       }
+
+       @Override
+       public void forceLoad()
+       {
+               super.forceLoad();
+       }
+
+       /**
+        * Helper method to take care of releasing resources associated with an
+        * actively loaded data set.
+        */
+       protected void onReleaseResources(Set<AbstractObject> o)
+       {
+               // For a simple List, there is nothing to do. For something like a Cursor,
+               // we would close it in this method. All resources associated with the
+               // Loader should be released here.
+       }
+}
index d15bac3..5303d7e 100644 (file)
-package org.netxms.ui.android.loaders;\r
-\r
-import org.netxms.client.NXCSession;\r
-import org.netxms.client.objects.AbstractObject;\r
-import org.netxms.ui.android.service.ClientConnectorService;\r
-\r
-import android.content.Context;\r
-import android.support.v4.content.AsyncTaskLoader;\r
-import android.util.Log;\r
-\r
-/**\r
- * Background loader for GenericObject objects. Notifies the fragment on job complete\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- *\r
- */\r
-\r
-public class GenericObjectLoader extends AsyncTaskLoader<AbstractObject>\r
-{\r
-       private static final String TAG = "nxclient/GenericObjectLoader";\r
-\r
-       private ClientConnectorService service = null;\r
-       private long nodeId = 0;\r
-       private AbstractObject obj = null;\r
-\r
-       /**\r
-        * @param context\r
-        */\r
-       public GenericObjectLoader(Context context)\r
-       {\r
-               // Loaders may be used across multiple Activitys (assuming they aren't\r
-               // bound to the LoaderManager), so NEVER hold a reference to the context\r
-               // directly. Doing so will cause you to leak an entire Activity's context.\r
-               // The superclass constructor will store a reference to the Application\r
-               // Context instead, and can be retrieved with a call to getContext().\r
-               super(context);\r
-       }\r
-\r
-       /**\r
-        * @param id\r
-        */\r
-       public void setObjId(long id)\r
-       {\r
-               this.nodeId = id;\r
-       }\r
-\r
-       /**\r
-        * @param service\r
-        */\r
-       public void setService(ClientConnectorService service)\r
-       {\r
-               this.service = service;\r
-               if (service != null)\r
-                       service.registerGenericObjectLoader(this);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.support.v4.content.AsyncTaskLoader#loadInBackground()\r
-        */\r
-       @Override\r
-       public AbstractObject loadInBackground()\r
-       {\r
-               try\r
-               {\r
-                       obj = null;\r
-                       if (service != null && service.getSession() != null)\r
-                       {\r
-                               service.getSession().syncObjectSet(new long[] { nodeId }, false, NXCSession.OBJECT_SYNC_WAIT);\r
-                               obj = service.getSession().findObjectById(nodeId);\r
-                       }\r
-                       else\r
-                               Log.d(TAG, "loadInBackground: service or session null!");\r
-               }\r
-               catch (Exception e)\r
-               {\r
-                       Log.e(TAG, "Exception while executing loadInBackground", e);\r
-               }\r
-               return obj;\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.support.v4.content.Loader#deliverResult(java.lang.Object)\r
-        */\r
-       @Override\r
-       public void deliverResult(AbstractObject newObj)\r
-       {\r
-               if (isReset())\r
-               {\r
-                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");\r
-                       // The Loader has been reset; ignore the result and invalidate the data.\r
-                       // This can happen when the Loader is reset while an asynchronous query\r
-                       // is working in the background. That is, when the background thread\r
-                       // finishes its work and attempts to deliver the results to the client,\r
-                       // it will see here that the Loader has been reset and discard any\r
-                       // resources associated with the new data as necessary.\r
-                       if (newObj != null)\r
-                       {\r
-                               onReleaseResources(newObj);\r
-                               return;\r
-                       }\r
-               }\r
-               // Hold a reference to the old data so it doesn't get garbage collected.\r
-               // The old data may still be in use (i.e. bound to an adapter, etc.), so\r
-               // we must protect it until the new data has been delivered.\r
-               AbstractObject oldObj = obj;\r
-               obj = newObj;\r
-\r
-               if (isStarted())\r
-               {\r
-                       // If the Loader is in a started state, have the superclass deliver the\r
-                       // results to the client.\r
-                       super.deliverResult(newObj);\r
-               }\r
-\r
-               // Invalidate the old data as we don't need it any more.\r
-               if (oldObj != null && oldObj != newObj)\r
-               {\r
-                       Log.i(TAG, "Releasing any old data associated with this Loader.");\r
-                       onReleaseResources(oldObj);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStartLoading()\r
-       {\r
-               if (obj != null)\r
-               {\r
-                       // Deliver any previously loaded data immediately.\r
-                       Log.i(TAG, "Delivering previously loaded data to the client...");\r
-                       deliverResult(obj);\r
-               }\r
-\r
-               // Register the observers that will notify the Loader when changes are made.\r
-               if (service != null)\r
-                       service.registerGenericObjectLoader(this);\r
-\r
-               if (takeContentChanged())\r
-               {\r
-                       // When the observer detects a new installed application, it will call\r
-                       // onContentChanged() on the Loader, which will cause the next call to\r
-                       // takeContentChanged() to return true. If this is ever the case (or if\r
-                       // the current data is null), we force a new load.\r
-                       Log.i(TAG, "A content change has been detected... so force load!");\r
-                       forceLoad();\r
-               }\r
-               else if (obj == null)\r
-               {\r
-                       // If the current data is null... then we should make it non-null! :)\r
-                       Log.i(TAG, "The current data is null... so force load!");\r
-                       forceLoad();\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected void onStopLoading()\r
-       {\r
-               // The Loader has been put in a stopped state, so we should attempt to\r
-               // cancel the current load (if there is one).\r
-               cancelLoad();\r
-\r
-               // Note that we leave the observer as is; Loaders in a stopped state\r
-               // should still monitor the data source for changes so that the Loader\r
-               // will know to force a new load if it is ever started again.\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.support.v4.content.Loader#onReset()\r
-        */\r
-       @Override\r
-       protected void onReset()\r
-       {\r
-               // Ensure the loader is stopped.\r
-               onStopLoading();\r
-\r
-               // At this point we can release the resources associated with 'apps'.\r
-               if (obj != null)\r
-               {\r
-                       onReleaseResources(obj);\r
-                       obj = null;\r
-               }\r
-\r
-               // The Loader is being reset, so we should stop monitoring for changes.\r
-               if (service != null)\r
-                       service.unregisterGenericObjectLoader(this);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.support.v4.content.AsyncTaskLoader#onCanceled(java.lang.Object)\r
-        */\r
-       @Override\r
-       public void onCanceled(AbstractObject o)\r
-       {\r
-               // Attempt to cancel the current asynchronous load.\r
-               super.onCanceled(o);\r
-\r
-               // The load has been canceled, so we should release the resources\r
-               // associated with 'obj'.\r
-               onReleaseResources(o);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.support.v4.content.Loader#forceLoad()\r
-        */\r
-       @Override\r
-       public void forceLoad()\r
-       {\r
-               super.forceLoad();\r
-       }\r
-\r
-       /**\r
-        * Helper method to take care of releasing resources associated with an\r
-        * actively loaded data set.\r
-        */\r
-       protected void onReleaseResources(AbstractObject o)\r
-       {\r
-               // For a simple List, there is nothing to do. For something like a Cursor,\r
-               // we would close it in this method. All resources associated with the\r
-               // Loader should be released here.\r
-       }\r
-}\r
+package org.netxms.ui.android.loaders;
+
+import org.netxms.client.NXCSession;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.android.service.ClientConnectorService;
+
+import android.content.Context;
+import android.support.v4.content.AsyncTaskLoader;
+import android.util.Log;
+
+/**
+ * Background loader for GenericObject objects. Notifies the fragment on job complete
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ *
+ */
+
+public class GenericObjectLoader extends AsyncTaskLoader<AbstractObject>
+{
+       private static final String TAG = "nxclient/GenericObjectLoader";
+
+       private ClientConnectorService service = null;
+       private long nodeId = 0;
+       private AbstractObject obj = null;
+
+       /**
+        * @param context
+        */
+       public GenericObjectLoader(Context context)
+       {
+               // Loaders may be used across multiple Activitys (assuming they aren't
+               // bound to the LoaderManager), so NEVER hold a reference to the context
+               // directly. Doing so will cause you to leak an entire Activity's context.
+               // The superclass constructor will store a reference to the Application
+               // Context instead, and can be retrieved with a call to getContext().
+               super(context);
+       }
+
+       /**
+        * @param id
+        */
+       public void setObjId(long id)
+       {
+               this.nodeId = id;
+       }
+
+       /**
+        * @param service
+        */
+       public void setService(ClientConnectorService service)
+       {
+               this.service = service;
+               if (service != null)
+                       service.registerGenericObjectLoader(this);
+       }
+
+       /* (non-Javadoc)
+        * @see android.support.v4.content.AsyncTaskLoader#loadInBackground()
+        */
+       @Override
+       public AbstractObject loadInBackground()
+       {
+               try
+               {
+                       obj = null;
+                       if (service != null && service.getSession() != null)
+                       {
+                               service.getSession().syncObjectSet(new long[] { nodeId }, false, NXCSession.OBJECT_SYNC_WAIT);
+                               obj = service.getSession().findObjectById(nodeId);
+                       }
+                       else
+                               Log.d(TAG, "loadInBackground: service or session null!");
+               }
+               catch (Exception e)
+               {
+                       Log.e(TAG, "Exception while executing loadInBackground", e);
+               }
+               return obj;
+       }
+
+       /* (non-Javadoc)
+        * @see android.support.v4.content.Loader#deliverResult(java.lang.Object)
+        */
+       @Override
+       public void deliverResult(AbstractObject newObj)
+       {
+               if (isReset())
+               {
+                       Log.w(TAG, "Warning! An async query came in while the Loader was reset!");
+                       // The Loader has been reset; ignore the result and invalidate the data.
+                       // This can happen when the Loader is reset while an asynchronous query
+                       // is working in the background. That is, when the background thread
+                       // finishes its work and attempts to deliver the results to the client,
+                       // it will see here that the Loader has been reset and discard any
+                       // resources associated with the new data as necessary.
+                       if (newObj != null)
+                       {
+                               onReleaseResources(newObj);
+                               return;
+                       }
+               }
+               // Hold a reference to the old data so it doesn't get garbage collected.
+               // The old data may still be in use (i.e. bound to an adapter, etc.), so
+               // we must protect it until the new data has been delivered.
+               AbstractObject oldObj = obj;
+               obj = newObj;
+
+               if (isStarted())
+               {
+                       // If the Loader is in a started state, have the superclass deliver the
+                       // results to the client.
+                       super.deliverResult(newObj);
+               }
+
+               // Invalidate the old data as we don't need it any more.
+               if (oldObj != null && oldObj != newObj)
+               {
+                       Log.i(TAG, "Releasing any old data associated with this Loader.");
+                       onReleaseResources(oldObj);
+               }
+       }
+
+       @Override
+       protected void onStartLoading()
+       {
+               if (obj != null)
+               {
+                       // Deliver any previously loaded data immediately.
+                       Log.i(TAG, "Delivering previously loaded data to the client...");
+                       deliverResult(obj);
+               }
+
+               // Register the observers that will notify the Loader when changes are made.
+               if (service != null)
+                       service.registerGenericObjectLoader(this);
+
+               if (takeContentChanged())
+               {
+                       // When the observer detects a new installed application, it will call
+                       // onContentChanged() on the Loader, which will cause the next call to
+                       // takeContentChanged() to return true. If this is ever the case (or if
+                       // the current data is null), we force a new load.
+                       Log.i(TAG, "A content change has been detected... so force load!");
+                       forceLoad();
+               }
+               else if (obj == null)
+               {
+                       // If the current data is null... then we should make it non-null! :)
+                       Log.i(TAG, "The current data is null... so force load!");
+                       forceLoad();
+               }
+       }
+
+       @Override
+       protected void onStopLoading()
+       {
+               // The Loader has been put in a stopped state, so we should attempt to
+               // cancel the current load (if there is one).
+               cancelLoad();
+
+               // Note that we leave the observer as is; Loaders in a stopped state
+               // should still monitor the data source for changes so that the Loader
+               // will know to force a new load if it is ever started again.
+       }
+
+       /* (non-Javadoc)
+        * @see android.support.v4.content.Loader#onReset()
+        */
+       @Override
+       protected void onReset()
+       {
+               // Ensure the loader is stopped.
+               onStopLoading();
+
+               // At this point we can release the resources associated with 'apps'.
+               if (obj != null)
+               {
+                       onReleaseResources(obj);
+                       obj = null;
+               }
+
+               // The Loader is being reset, so we should stop monitoring for changes.
+               if (service != null)
+                       service.unregisterGenericObjectLoader(this);
+       }
+
+       /* (non-Javadoc)
+        * @see android.support.v4.content.AsyncTaskLoader#onCanceled(java.lang.Object)
+        */
+       @Override
+       public void onCanceled(AbstractObject o)
+       {
+               // Attempt to cancel the current asynchronous load.
+               super.onCanceled(o);
+
+               // The load has been canceled, so we should release the resources
+               // associated with 'obj'.
+               onReleaseResources(o);
+       }
+
+       /* (non-Javadoc)
+        * @see android.support.v4.content.Loader#forceLoad()
+        */
+       @Override
+       public void forceLoad()
+       {
+               super.forceLoad();
+       }
+
+       /**
+        * Helper method to take care of releasing resources associated with an
+        * actively loaded data set.
+        */
+       protected void onReleaseResources(AbstractObject o)
+       {
+               // For a simple List, there is nothing to do. For something like a Cursor,
+               // we would close it in this method. All resources associated with the
+               // Loader should be released here.
+       }
+}
index 503caf0..2aa156a 100644 (file)
-package org.netxms.ui.android.main.activities;\r
-\r
-import java.util.ArrayList;\r
-\r
-import org.achartengine.GraphicalView;\r
-import org.netxms.client.datacollection.DciData;\r
-import org.netxms.client.datacollection.DciDataRow;\r
-import org.netxms.client.datacollection.GraphItem;\r
-import org.netxms.ui.android.R;\r
-\r
-import android.app.ProgressDialog;\r
-import android.content.ComponentName;\r
-import android.os.AsyncTask;\r
-import android.os.Bundle;\r
-import android.os.IBinder;\r
-import android.util.Log;\r
-import android.widget.LinearLayout;\r
-import android.widget.TextView;\r
-\r
-public abstract class AbstractComparisonChart extends AbstractClientActivity\r
-{\r
-       private static final String TAG = "nxclient/AbstractComparisonChart";\r
-\r
-       protected ArrayList<Integer> colorList = null;\r
-       protected GraphItem[] items = null;\r
-       protected double[] values = null;\r
-       protected String graphTitle = "";\r
-\r
-       private GraphicalView graphView = null;\r
-       private ProgressDialog dialog;\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)\r
-        */\r
-       @Override\r
-       protected void onCreateStep2(Bundle savedInstanceState)\r
-       {\r
-               dialog = new ProgressDialog(this);\r
-               setContentView(R.layout.graphics);\r
-               int numItems = getIntent().getIntExtra("numItems", 0);\r
-               if (numItems > 0)\r
-               {\r
-                       items = new GraphItem[numItems];\r
-                       values = new double[numItems];\r
-                       ArrayList<Integer> nodeIdList = getIntent().getIntegerArrayListExtra("nodeIdList");\r
-                       ArrayList<Integer> dciIdList = getIntent().getIntegerArrayListExtra("dciIdList");\r
-                       ArrayList<String> nameList = getIntent().getStringArrayListExtra("nameList");\r
-                       colorList = getIntent().getIntegerArrayListExtra("colorList");\r
-                       for (int i = 0; i < numItems; i++)\r
-                       {\r
-                               items[i] = new GraphItem();\r
-                               items[i].setNodeId(nodeIdList.get(i));\r
-                               items[i].setDciId(dciIdList.get(i));\r
-                               items[i].setDescription(nameList.get(i));\r
-                       }\r
-                       graphTitle = getIntent().getStringExtra("graphTitle");\r
-               }\r
-               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);\r
-               title.setText(R.string.graph_title);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceConnected(android.content.ComponentName, android.os.IBinder)\r
-        */\r
-       @Override\r
-       public void onServiceConnected(ComponentName name, IBinder binder)\r
-       {\r
-               super.onServiceConnected(name, binder);\r
-               refreshGraph();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceDisconnected(android.content.ComponentName)\r
-        */\r
-       @Override\r
-       public void onServiceDisconnected(ComponentName name)\r
-       {\r
-               super.onServiceDisconnected(name);\r
-       }\r
-\r
-       /**\r
-        * Refresh node graph\r
-        */\r
-       public void refreshGraph()\r
-       {\r
-               if (graphTitle != null)\r
-               {\r
-                       TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);\r
-                       title.setText(graphTitle);\r
-               }\r
-               new LoadDataTask().execute();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onDestroy()\r
-        */\r
-       @Override\r
-       protected void onDestroy()\r
-       {\r
-               service.registerNodeBrowser(null);\r
-               super.onDestroy();\r
-       }\r
-\r
-       /**\r
-        * Convert to Android color format (swap RGB and add alpha)\r
-        */\r
-       protected int toAndroidColor(int color)\r
-       {\r
-               return 0xFF000000 | ((color & 0x0000FF) << 16) | (color & 0x00FF00) | ((color & 0xFF0000) >> 16); // Alpha | R | G | B\r
-       }\r
-\r
-       /**\r
-        * @return\r
-        */\r
-       protected abstract GraphicalView buildGraphView();\r
-\r
-       /**\r
-        * Internal task for loading DCI data\r
-        */\r
-       class LoadDataTask extends AsyncTask<Object, Void, DciData[]>\r
-       {\r
-               /* (non-Javadoc)\r
-                * @see android.os.AsyncTask#onPreExecute()\r
-                */\r
-               @Override\r
-               protected void onPreExecute()\r
-               {\r
-                       dialog.setMessage(getString(R.string.progress_gathering_data));\r
-                       dialog.setIndeterminate(true);\r
-                       dialog.setCancelable(false);\r
-                       dialog.show();\r
-               }\r
-\r
-               /* (non-Javadoc)\r
-                * @see android.os.AsyncTask#doInBackground(Params[])\r
-                */\r
-               @Override\r
-               protected DciData[] doInBackground(Object... params)\r
-               {\r
-                       DciData[] dciData = null;\r
-                       try\r
-                       {\r
-                               if (items.length > 0)\r
-                               {\r
-                                       dciData = new DciData[items.length];\r
-                                       for (int i = 0; i < dciData.length; i++)\r
-                                       {\r
-                                               dciData[i] = new DciData(0, 0);\r
-                                               dciData[i] = service.getSession().getCollectedData(items[i].getNodeId(), items[i].getDciId(), null, null, 1);\r
-                                       }\r
-                               }\r
-                       }\r
-                       catch (Exception e)\r
-                       {\r
-                               Log.e(TAG, "Exception while executing LoadDataTask.doInBackground", e);\r
-                               dciData = null;\r
-                       }\r
-                       return dciData;\r
-               }\r
-\r
-               /* (non-Javadoc)\r
-                * @see android.os.AsyncTask#onPostExecute(java.lang.Object)\r
-                */\r
-               @Override\r
-               protected void onPostExecute(DciData[] result)\r
-               {\r
-                       if (result != null)\r
-                       {\r
-                               for (int i = 0; (i < result.length) && (i < values.length); i++)\r
-                               {\r
-                                       DciDataRow value = result[i].getLastValue();\r
-                                       values[i] = (value != null) ? value.getValueAsDouble() : 0.0;\r
-                               }\r
-\r
-                               LinearLayout layout = (LinearLayout)findViewById(R.id.graphics);\r
-                               if (layout != null)\r
-                               {\r
-                                       if (graphView != null)\r
-                                               layout.removeView(graphView);\r
-                                       graphView = buildGraphView();\r
-                                       layout.addView(graphView);\r
-                               }\r
-                       }\r
-                       dialog.cancel();\r
-               }\r
-       }\r
-}\r
+package org.netxms.ui.android.main.activities;
+
+import java.util.ArrayList;
+
+import org.achartengine.GraphicalView;
+import org.netxms.client.datacollection.DciData;
+import org.netxms.client.datacollection.DciDataRow;
+import org.netxms.client.datacollection.GraphItem;
+import org.netxms.ui.android.R;
+
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public abstract class AbstractComparisonChart extends AbstractClientActivity
+{
+       private static final String TAG = "nxclient/AbstractComparisonChart";
+
+       protected ArrayList<Integer> colorList = null;
+       protected GraphItem[] items = null;
+       protected double[] values = null;
+       protected String graphTitle = "";
+
+       private GraphicalView graphView = null;
+       private ProgressDialog dialog;
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)
+        */
+       @Override
+       protected void onCreateStep2(Bundle savedInstanceState)
+       {
+               dialog = new ProgressDialog(this);
+               setContentView(R.layout.graphics);
+               int numItems = getIntent().getIntExtra("numItems", 0);
+               if (numItems > 0)
+               {
+                       items = new GraphItem[numItems];
+                       values = new double[numItems];
+                       ArrayList<Integer> nodeIdList = getIntent().getIntegerArrayListExtra("nodeIdList");
+                       ArrayList<Integer> dciIdList = getIntent().getIntegerArrayListExtra("dciIdList");
+                       ArrayList<String> nameList = getIntent().getStringArrayListExtra("nameList");
+                       colorList = getIntent().getIntegerArrayListExtra("colorList");
+                       for (int i = 0; i < numItems; i++)
+                       {
+                               items[i] = new GraphItem();
+                               items[i].setNodeId(nodeIdList.get(i));
+                               items[i].setDciId(dciIdList.get(i));
+                               items[i].setDescription(nameList.get(i));
+                       }
+                       graphTitle = getIntent().getStringExtra("graphTitle");
+               }
+               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
+               title.setText(R.string.graph_title);
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceConnected(android.content.ComponentName, android.os.IBinder)
+        */
+       @Override
+       public void onServiceConnected(ComponentName name, IBinder binder)
+       {
+               super.onServiceConnected(name, binder);
+               refreshGraph();
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceDisconnected(android.content.ComponentName)
+        */
+       @Override
+       public void onServiceDisconnected(ComponentName name)
+       {
+               super.onServiceDisconnected(name);
+       }
+
+       /**
+        * Refresh node graph
+        */
+       public void refreshGraph()
+       {
+               if (graphTitle != null)
+               {
+                       TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
+                       title.setText(graphTitle);
+               }
+               new LoadDataTask().execute();
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onDestroy()
+        */
+       @Override
+       protected void onDestroy()
+       {
+               service.registerNodeBrowser(null);
+               super.onDestroy();
+       }
+
+       /**
+        * Convert to Android color format (swap RGB and add alpha)
+        */
+       protected int toAndroidColor(int color)
+       {
+               return 0xFF000000 | ((color & 0x0000FF) << 16) | (color & 0x00FF00) | ((color & 0xFF0000) >> 16); // Alpha | R | G | B
+       }
+
+       /**
+        * @return
+        */
+       protected abstract GraphicalView buildGraphView();
+
+       /**
+        * Internal task for loading DCI data
+        */
+       class LoadDataTask extends AsyncTask<Object, Void, DciData[]>
+       {
+               /* (non-Javadoc)
+                * @see android.os.AsyncTask#onPreExecute()
+                */
+               @Override
+               protected void onPreExecute()
+               {
+                       dialog.setMessage(getString(R.string.progress_gathering_data));
+                       dialog.setIndeterminate(true);
+                       dialog.setCancelable(false);
+                       dialog.show();
+               }
+
+               /* (non-Javadoc)
+                * @see android.os.AsyncTask#doInBackground(Params[])
+                */
+               @Override
+               protected DciData[] doInBackground(Object... params)
+               {
+                       DciData[] dciData = null;
+                       try
+                       {
+                               if (items.length > 0)
+                               {
+                                       dciData = new DciData[items.length];
+                                       for (int i = 0; i < dciData.length; i++)
+                                       {
+                                               dciData[i] = new DciData(0, 0);
+                                               dciData[i] = service.getSession().getCollectedData(items[i].getNodeId(), items[i].getDciId(), null, null, 1);
+                                       }
+                               }
+                       }
+                       catch (Exception e)
+                       {
+                               Log.e(TAG, "Exception while executing LoadDataTask.doInBackground", e);
+                               dciData = null;
+                       }
+                       return dciData;
+               }
+
+               /* (non-Javadoc)
+                * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
+                */
+               @Override
+               protected void onPostExecute(DciData[] result)
+               {
+                       if (result != null)
+                       {
+                               for (int i = 0; (i < result.length) && (i < values.length); i++)
+                               {
+                                       DciDataRow value = result[i].getLastValue();
+                                       values[i] = (value != null) ? value.getValueAsDouble() : 0.0;
+                               }
+
+                               LinearLayout layout = (LinearLayout)findViewById(R.id.graphics);
+                               if (layout != null)
+                               {
+                                       if (graphView != null)
+                                               layout.removeView(graphView);
+                                       graphView = buildGraphView();
+                                       layout.addView(graphView);
+                               }
+                       }
+                       dialog.cancel();
+               }
+       }
+}
index 8b7c915..e7f875e 100644 (file)
-package org.netxms.ui.android.main.activities;\r
-\r
-import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.json.JSONArray;\r
-import org.json.JSONException;\r
-import org.netxms.client.MacAddress;\r
-import org.netxms.client.MacAddressFormatException;\r
-import org.netxms.client.NXCException;\r
-import org.netxms.client.NXCSession;\r
-import org.netxms.client.objects.Interface;\r
-import org.netxms.client.objects.Node;\r
-import org.netxms.client.topology.ConnectionPoint;\r
-import org.netxms.ui.android.R;\r
-import org.netxms.ui.android.main.adapters.ConnectionPointListAdapter;\r
-import org.netxms.ui.android.tools.BarcodeScannerIntegrator;\r
-import org.netxms.ui.android.tools.BarcodeScannerIntentResult;\r
-\r
-import android.app.AlertDialog;\r
-import android.app.ProgressDialog;\r
-import android.content.ComponentName;\r
-import android.content.Context;\r
-import android.content.DialogInterface;\r
-import android.content.Intent;\r
-import android.content.SharedPreferences;\r
-import android.content.res.Resources;\r
-import android.os.AsyncTask;\r
-import android.os.Bundle;\r
-import android.os.IBinder;\r
-import android.preference.PreferenceManager;\r
-import android.util.Log;\r
-import android.view.ContextMenu;\r
-import android.view.ContextMenu.ContextMenuInfo;\r
-import android.view.KeyEvent;\r
-import android.view.MenuItem;\r
-import android.view.View;\r
-import android.view.View.OnClickListener;\r
-import android.view.View.OnKeyListener;\r
-import android.widget.AdapterView;\r
-import android.widget.AdapterView.AdapterContextMenuInfo;\r
-import android.widget.Button;\r
-import android.widget.EditText;\r
-import android.widget.ListView;\r
-import android.widget.TextView;\r
-\r
-/**\r
- * MAC address search result browser\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- *\r
- */\r
-\r
-public class ConnectionPointBrowser extends AbstractClientActivity\r
-{\r
-       private static final String TAG = "nxclient/ConnectionPointBrowser";\r
-       private static final String CPLIST_KEY = "ConnectionPointList";\r
-       private final int maxConnectionPoints = 50;\r
-       private List<String> cpList = new ArrayList<String>(0);\r
-       private EditText editText;\r
-       private ListView listView;\r
-       private ConnectionPointListAdapter adapter;\r
-       private int nodeId = 0;\r
-       private ProgressDialog dialog;\r
-       private Resources r;\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)\r
-        */\r
-       @Override\r
-       protected void onCreateStep2(Bundle savedInstanceState)\r
-       {\r
-               dialog = new ProgressDialog(this);\r
-               r = getResources();\r
-               setContentView(R.layout.connection_point_view);\r
-\r
-               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);\r
-               title.setText(R.string.connection_point_title);\r
-               // keeps current list of alarms as datasource for listview\r
-               adapter = new ConnectionPointListAdapter(this);\r
-               listView = (ListView)findViewById(R.id.ConnectionPointList);\r
-               listView.setAdapter(adapter);\r
-               registerForContextMenu(listView);\r
-\r
-               editText = (EditText)findViewById(R.id.MacAddressToSearch);\r
-               editText.setOnKeyListener(new OnKeyListener()\r
-               {\r
-                       @Override\r
-                       public boolean onKey(View v, int keyCode, KeyEvent event)\r
-                       {\r
-                               if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) // If the event is a key-down event on the "enter" button\r
-                               {\r
-                                       nodeId = 0;\r
-                                       startSearch();\r
-                                       return true;\r
-                               }\r
-                               return false;\r
-                       }\r
-               });\r
-\r
-               final Button scanButton = (Button)findViewById(R.id.ScanBarcode);\r
-               scanButton.setOnClickListener(new OnClickListener()\r
-               {\r
-                       @Override\r
-                       public void onClick(View v)\r
-                       {\r
-                               nodeId = 0;\r
-                               BarcodeScannerIntegrator integrator = new BarcodeScannerIntegrator(ConnectionPointBrowser.this);\r
-                               integrator.initiateScan();\r
-                       }\r
-               });\r
-\r
-               nodeId = getIntent().getIntExtra("nodeId", 0);\r
-               cpList = retrievePreferences(this, CPLIST_KEY, maxConnectionPoints);\r
-               adapter.setConnectionPoint(cpList);\r
-               adapter.notifyDataSetChanged();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onActivityResult(int, int, android.content.Intent)\r
-        */\r
-       @Override\r
-       protected void onActivityResult(int requestCode, int resultCode, Intent data)\r
-       {\r
-               Log.w(TAG, "onActivityResult: rq=" + requestCode + " result=" + resultCode);\r
-               BarcodeScannerIntentResult scanResult = BarcodeScannerIntegrator.parseActivityResult(requestCode, resultCode, data);\r
-               if (scanResult != null)\r
-               {\r
-                       editText.setText(scanResult.getContents());\r
-                       startSearch();\r
-               }\r
-               super.onActivityResult(requestCode, resultCode, data);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName, android.os.IBinder)\r
-        */\r
-       @Override\r
-       public void onServiceConnected(ComponentName name, IBinder binder)\r
-       {\r
-               super.onServiceConnected(name, binder);\r
-               if (nodeId != 0)\r
-                       startSearch();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName)\r
-        */\r
-       @Override\r
-       public void onServiceDisconnected(ComponentName name)\r
-       {\r
-               super.onServiceDisconnected(name);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)\r
-        */\r
-       @Override\r
-       public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)\r
-       {\r
-               android.view.MenuInflater inflater = getMenuInflater();\r
-               inflater.inflate(R.menu.connection_point_actions, menu);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)\r
-        */\r
-       @Override\r
-       public boolean onContextItemSelected(MenuItem item)\r
-       {\r
-               // get selected item\r
-               AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();\r
-               final int position = info.position;\r
-\r
-               if (item.getItemId() == R.id.connection_point_delete_one)\r
-               {\r
-                       new AlertDialog.Builder(this)\r
-                                       .setIcon(android.R.drawable.ic_dialog_alert)\r
-                                       .setTitle(R.string.confirm_tool_execution)\r
-                                       .setMessage(r.getString(R.string.connection_point_confirm_delete_one))\r
-                                       .setCancelable(true)\r
-                                       .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener()\r
-                                       {\r
-                                               @Override\r
-                                               public void onClick(DialogInterface dialog, int which)\r
-                                               {\r
-                                                       cpList.remove(position);\r
-                                                       refreshList();\r
-                                               }\r
-                                       })\r
-                                       .setNegativeButton(R.string.no, null)\r
-                                       .show();\r
-               }\r
-               else if (item.getItemId() == R.id.connection_point_delete_all)\r
-               {\r
-                       new AlertDialog.Builder(this)\r
-                                       .setIcon(android.R.drawable.ic_dialog_alert)\r
-                                       .setTitle(R.string.confirm_tool_execution)\r
-                                       .setMessage(r.getString(R.string.connection_point_confirm_delete_all))\r
-                                       .setCancelable(true)\r
-                                       .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener()\r
-                                       {\r
-                                               @Override\r
-                                               public void onClick(DialogInterface dialog, int which)\r
-                                               {\r
-                                                       cpList.clear();\r
-                                                       refreshList();\r
-                                               }\r
-                                       })\r
-                                       .setNegativeButton(R.string.no, null)\r
-                                       .show();\r
-               }\r
-               else\r
-               {\r
-                       return super.onContextItemSelected(item);\r
-               }\r
-               return true;\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onDestroy()\r
-        */\r
-       @Override\r
-       protected void onDestroy()\r
-       {\r
-               super.onDestroy();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onStop()\r
-        */\r
-       @Override\r
-       protected void onStop()\r
-       {\r
-               super.onStop();\r
-               storePreferences(this, CPLIST_KEY, cpList, maxConnectionPoints);\r
-       }\r
-\r
-       private void refreshList()\r
-       {\r
-               adapter.setConnectionPoint(cpList);\r
-               adapter.notifyDataSetChanged();\r
-       }\r
-\r
-       /**\r
-        * Store connection points result list\r
-        */\r
-       private void storePreferences(Context context, String key, List<String> values, int maxValues)\r
-       {\r
-               SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\r
-               SharedPreferences.Editor editor = prefs.edit();\r
-               JSONArray a = new JSONArray();\r
-               for (int i = 0; i < Math.min(values.size(), maxValues); i++)\r
-                       a.put(values.get(i));\r
-               if (!values.isEmpty())\r
-                       editor.putString(key, a.toString());\r
-               else\r
-                       editor.putString(key, null);\r
-               editor.commit();\r
-       }\r
-\r
-       /**\r
-        * Retrieve connection points result list\r
-        */\r
-       private ArrayList<String> retrievePreferences(Context context, String key, int maxValues)\r
-       {\r
-               SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\r
-               String json = prefs.getString(key, null);\r
-               ArrayList<String> values = new ArrayList<String>();\r
-               if (json != null)\r
-                       try\r
-                       {\r
-                               JSONArray a = new JSONArray(json);\r
-                               for (int i = 0; i < Math.min(a.length(), maxValues); i++)\r
-                                       values.add(a.optString(i));\r
-                       }\r
-                       catch (JSONException e)\r
-                       {\r
-                               e.printStackTrace();\r
-                       }\r
-               return values;\r
-       }\r
-\r
-       /**\r
-        * Start MAC address search\r
-        */\r
-       private void startSearch()\r
-       {\r
-               new SearchConnectionPointTask(nodeId, editText.getText().toString()).execute();\r
-       }\r
-\r
-       /**\r
-        * Internal task for loading info for MAC address search\r
-        */\r
-       private class SearchConnectionPointTask extends AsyncTask<Object, Void, String>\r
-       {\r
-               private final int nodeId;\r
-               private final String macAddress;\r
-\r
-               SearchConnectionPointTask(int nodeId, String macAddress)\r
-               {\r
-                       this.nodeId = nodeId;\r
-                       this.macAddress = macAddress;\r
-               }\r
-\r
-               /* (non-Javadoc)\r
-                * @see android.os.AsyncTask#onPreExecute()\r
-                */\r
-               @Override\r
-               protected void onPreExecute()\r
-               {\r
-                       dialog.setMessage(getString(R.string.progress_gathering_data));\r
-                       dialog.setIndeterminate(true);\r
-                       dialog.setCancelable(false);\r
-                       dialog.show();\r
-               }\r
-\r
-               /* (non-Javadoc)\r
-                * @see android.os.AsyncTask#doInBackground(Params[])\r
-                */\r
-               @Override\r
-               protected String doInBackground(Object... params)\r
-               {\r
-                       String string = nodeId != 0 ? r.getString(R.string.connection_point_notfound) : r.getString(R.string.connection_point_macaddress_notfound, macAddress);\r
-                       if (service != null)\r
-                       {\r
-                               NXCSession session = service.getSession();\r
-                               if (session != null)\r
-                               {\r
-                                       ConnectionPoint cp = null;\r
-                                       Node host = null;\r
-                                       Node bridge = null;\r
-                                       Interface iface = null;\r
-                                       try\r
-                                       {\r
-                                               cp = nodeId != 0 ? session.findConnectionPoint(nodeId) : session.findConnectionPoint(MacAddress.parseMacAddress(macAddress));\r
-                                               if (cp != null)\r
-                                               {\r
-                                                       session.syncMissingObjects(new long[] { cp.getLocalNodeId(), cp.getNodeId(), cp.getInterfaceId() }, false, NXCSession.OBJECT_SYNC_WAIT);\r
-                                                       host = (Node)session.findObjectById(cp.getLocalNodeId());\r
-                                                       bridge = (Node)session.findObjectById(cp.getNodeId());\r
-                                                       iface = (Interface)session.findObjectById(cp.getInterfaceId());\r
-                                               }\r
-                                               else\r
-                                               {\r
-                                                       session.syncMissingObjects(new long[] { nodeId }, false, NXCSession.OBJECT_SYNC_WAIT);\r
-                                                       host = (Node)session.findObjectById(nodeId);\r
-                                               }\r
-                                       }\r
-                                       catch (MacAddressFormatException e)\r
-                                       {\r
-                                               Log.e(TAG, "MacAddressFormatException while executing searchMacAddress", e);\r
-                                               string = r.getString(R.string.connection_point_invalid, macAddress);\r
-                                       }\r
-                                       catch (NXCException e)\r
-                                       {\r
-                                               Log.e(TAG, "NXCException while executing syncMissingObjects", e);\r
-                                       }\r
-                                       catch (IOException e)\r
-                                       {\r
-                                               Log.e(TAG, "IOException while executing syncMissingObjects", e);\r
-                                       }\r
-\r
-                                       if ((bridge != null) && (iface != null))\r
-                                               string = r.getString(R.string.connection_point_info, host != null ? " " + host.getObjectName() : "", cp.getLocalMacAddress().toString(), bridge.getObjectName(), iface.getObjectName());\r
-                                       else if (host != null)\r
-                                               string = r.getString(R.string.connection_point_nodeid_notfound, host.getObjectName());\r
-                               }\r
-                       }\r
-                       return string;\r
-               }\r
-\r
-               /* (non-Javadoc)\r
-                * @see android.os.AsyncTask#onPostExecute(java.lang.Object)\r
-                */\r
-               @Override\r
-               protected void onPostExecute(String result)\r
-               {\r
-                       dialog.cancel();\r
-                       if (result != null)\r
-                       {\r
-                               cpList.add(0, result);\r
-                               if (cpList.size() > maxConnectionPoints)\r
-                                       cpList.remove(maxConnectionPoints);\r
-                               refreshList();\r
-                       }\r
-               }\r
-       }\r
-}\r
+package org.netxms.ui.android.main.activities;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.netxms.client.MacAddress;
+import org.netxms.client.MacAddressFormatException;
+import org.netxms.client.NXCException;
+import org.netxms.client.NXCSession;
+import org.netxms.client.objects.Interface;
+import org.netxms.client.objects.Node;
+import org.netxms.client.topology.ConnectionPoint;
+import org.netxms.ui.android.R;
+import org.netxms.ui.android.main.adapters.ConnectionPointListAdapter;
+import org.netxms.ui.android.tools.BarcodeScannerIntegrator;
+import org.netxms.ui.android.tools.BarcodeScannerIntentResult;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * MAC address search result browser
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ *
+ */
+
+public class ConnectionPointBrowser extends AbstractClientActivity
+{
+       private static final String TAG = "nxclient/ConnectionPointBrowser";
+       private static final String CPLIST_KEY = "ConnectionPointList";
+       private final int maxConnectionPoints = 50;
+       private List<String> cpList = new ArrayList<String>(0);
+       private EditText editText;
+       private ListView listView;
+       private ConnectionPointListAdapter adapter;
+       private int nodeId = 0;
+       private ProgressDialog dialog;
+       private Resources r;
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)
+        */
+       @Override
+       protected void onCreateStep2(Bundle savedInstanceState)
+       {
+               dialog = new ProgressDialog(this);
+               r = getResources();
+               setContentView(R.layout.connection_point_view);
+
+               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
+               title.setText(R.string.connection_point_title);
+               // keeps current list of alarms as datasource for listview
+               adapter = new ConnectionPointListAdapter(this);
+               listView = (ListView)findViewById(R.id.ConnectionPointList);
+               listView.setAdapter(adapter);
+               registerForContextMenu(listView);
+
+               editText = (EditText)findViewById(R.id.MacAddressToSearch);
+               editText.setOnKeyListener(new OnKeyListener()
+               {
+                       @Override
+                       public boolean onKey(View v, int keyCode, KeyEvent event)
+                       {
+                               if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) // If the event is a key-down event on the "enter" button
+                               {
+                                       nodeId = 0;
+                                       startSearch();
+                                       return true;
+                               }
+                               return false;
+                       }
+               });
+
+               final Button scanButton = (Button)findViewById(R.id.ScanBarcode);
+               scanButton.setOnClickListener(new OnClickListener()
+               {
+                       @Override
+                       public void onClick(View v)
+                       {
+                               nodeId = 0;
+                               BarcodeScannerIntegrator integrator = new BarcodeScannerIntegrator(ConnectionPointBrowser.this);
+                               integrator.initiateScan();
+                       }
+               });
+
+               nodeId = getIntent().getIntExtra("nodeId", 0);
+               cpList = retrievePreferences(this, CPLIST_KEY, maxConnectionPoints);
+               adapter.setConnectionPoint(cpList);
+               adapter.notifyDataSetChanged();
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
+        */
+       @Override
+       protected void onActivityResult(int requestCode, int resultCode, Intent data)
+       {
+               Log.w(TAG, "onActivityResult: rq=" + requestCode + " result=" + resultCode);
+               BarcodeScannerIntentResult scanResult = BarcodeScannerIntegrator.parseActivityResult(requestCode, resultCode, data);
+               if (scanResult != null)
+               {
+                       editText.setText(scanResult.getContents());
+                       startSearch();
+               }
+               super.onActivityResult(requestCode, resultCode, data);
+       }
+
+       /* (non-Javadoc)
+        * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName, android.os.IBinder)
+        */
+       @Override
+       public void onServiceConnected(ComponentName name, IBinder binder)
+       {
+               super.onServiceConnected(name, binder);
+               if (nodeId != 0)
+                       startSearch();
+       }
+
+       /* (non-Javadoc)
+        * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName)
+        */
+       @Override
+       public void onServiceDisconnected(ComponentName name)
+       {
+               super.onServiceDisconnected(name);
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
+        */
+       @Override
+       public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
+       {
+               android.view.MenuInflater inflater = getMenuInflater();
+               inflater.inflate(R.menu.connection_point_actions, menu);
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
+        */
+       @Override
+       public boolean onContextItemSelected(MenuItem item)
+       {
+               // get selected item
+               AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
+               final int position = info.position;
+
+               if (item.getItemId() == R.id.connection_point_delete_one)
+               {
+                       new AlertDialog.Builder(this)
+                                       .setIcon(android.R.drawable.ic_dialog_alert)
+                                       .setTitle(R.string.confirm_tool_execution)
+                                       .setMessage(r.getString(R.string.connection_point_confirm_delete_one))
+                                       .setCancelable(true)
+                                       .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener()
+                                       {
+                                               @Override
+                                               public void onClick(DialogInterface dialog, int which)
+                                               {
+                                                       cpList.remove(position);
+                                                       refreshList();
+                                               }
+                                       })
+                                       .setNegativeButton(R.string.no, null)
+                                       .show();
+               }
+               else if (item.getItemId() == R.id.connection_point_delete_all)
+               {
+                       new AlertDialog.Builder(this)
+                                       .setIcon(android.R.drawable.ic_dialog_alert)
+                                       .setTitle(R.string.confirm_tool_execution)
+                                       .setMessage(r.getString(R.string.connection_point_confirm_delete_all))
+                                       .setCancelable(true)
+                                       .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener()
+                                       {
+                                               @Override
+                                               public void onClick(DialogInterface dialog, int which)
+                                               {
+                                                       cpList.clear();
+                                                       refreshList();
+                                               }
+                                       })
+                                       .setNegativeButton(R.string.no, null)
+                                       .show();
+               }
+               else
+               {
+                       return super.onContextItemSelected(item);
+               }
+               return true;
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onDestroy()
+        */
+       @Override
+       protected void onDestroy()
+       {
+               super.onDestroy();
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onStop()
+        */
+       @Override
+       protected void onStop()
+       {
+               super.onStop();
+               storePreferences(this, CPLIST_KEY, cpList, maxConnectionPoints);
+       }
+
+       private void refreshList()
+       {
+               adapter.setConnectionPoint(cpList);
+               adapter.notifyDataSetChanged();
+       }
+
+       /**
+        * Store connection points result list
+        */
+       private void storePreferences(Context context, String key, List<String> values, int maxValues)
+       {
+               SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+               SharedPreferences.Editor editor = prefs.edit();
+               JSONArray a = new JSONArray();
+               for (int i = 0; i < Math.min(values.size(), maxValues); i++)
+                       a.put(values.get(i));
+               if (!values.isEmpty())
+                       editor.putString(key, a.toString());
+               else
+                       editor.putString(key, null);
+               editor.commit();
+       }
+
+       /**
+        * Retrieve connection points result list
+        */
+       private ArrayList<String> retrievePreferences(Context context, String key, int maxValues)
+       {
+               SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+               String json = prefs.getString(key, null);
+               ArrayList<String> values = new ArrayList<String>();
+               if (json != null)
+                       try
+                       {
+                               JSONArray a = new JSONArray(json);
+                               for (int i = 0; i < Math.min(a.length(), maxValues); i++)
+                                       values.add(a.optString(i));
+                       }
+                       catch (JSONException e)
+                       {
+                               e.printStackTrace();
+                       }
+               return values;
+       }
+
+       /**
+        * Start MAC address search
+        */
+       private void startSearch()
+       {
+               new SearchConnectionPointTask(nodeId, editText.getText().toString()).execute();
+       }
+
+       /**
+        * Internal task for loading info for MAC address search
+        */
+       private class SearchConnectionPointTask extends AsyncTask<Object, Void, String>
+       {
+               private final int nodeId;
+               private final String macAddress;
+
+               SearchConnectionPointTask(int nodeId, String macAddress)
+               {
+                       this.nodeId = nodeId;
+                       this.macAddress = macAddress;
+               }
+
+               /* (non-Javadoc)
+                * @see android.os.AsyncTask#onPreExecute()
+                */
+               @Override
+               protected void onPreExecute()
+               {
+                       dialog.setMessage(getString(R.string.progress_gathering_data));
+                       dialog.setIndeterminate(true);
+                       dialog.setCancelable(false);
+                       dialog.show();
+               }
+
+               /* (non-Javadoc)
+                * @see android.os.AsyncTask#doInBackground(Params[])
+                */
+               @Override
+               protected String doInBackground(Object... params)
+               {
+                       String string = nodeId != 0 ? r.getString(R.string.connection_point_notfound) : r.getString(R.string.connection_point_macaddress_notfound, macAddress);
+                       if (service != null)
+                       {
+                               NXCSession session = service.getSession();
+                               if (session != null)
+                               {
+                                       ConnectionPoint cp = null;
+                                       Node host = null;
+                                       Node bridge = null;
+                                       Interface iface = null;
+                                       try
+                                       {
+                                               cp = nodeId != 0 ? session.findConnectionPoint(nodeId) : session.findConnectionPoint(MacAddress.parseMacAddress(macAddress));
+                                               if (cp != null)
+                                               {
+                                                       session.syncMissingObjects(new long[] { cp.getLocalNodeId(), cp.getNodeId(), cp.getInterfaceId() }, false, NXCSession.OBJECT_SYNC_WAIT);
+                                                       host = (Node)session.findObjectById(cp.getLocalNodeId());
+                                                       bridge = (Node)session.findObjectById(cp.getNodeId());
+                                                       iface = (Interface)session.findObjectById(cp.getInterfaceId());
+                                               }
+                                               else
+                                               {
+                                                       session.syncMissingObjects(new long[] { nodeId }, false, NXCSession.OBJECT_SYNC_WAIT);
+                                                       host = (Node)session.findObjectById(nodeId);
+                                               }
+                                       }
+                                       catch (MacAddressFormatException e)
+                                       {
+                                               Log.e(TAG, "MacAddressFormatException while executing searchMacAddress", e);
+                                               string = r.getString(R.string.connection_point_invalid, macAddress);
+                                       }
+                                       catch (NXCException e)
+                                       {
+                                               Log.e(TAG, "NXCException while executing syncMissingObjects", e);
+                                       }
+                                       catch (IOException e)
+                                       {
+                                               Log.e(TAG, "IOException while executing syncMissingObjects", e);
+                                       }
+
+                                       if ((bridge != null) && (iface != null))
+                                               string = r.getString(R.string.connection_point_info, host != null ? " " + host.getObjectName() : "", cp.getLocalMacAddress().toString(), bridge.getObjectName(), iface.getObjectName());
+                                       else if (host != null)
+                                               string = r.getString(R.string.connection_point_nodeid_notfound, host.getObjectName());
+                               }
+                       }
+                       return string;
+               }
+
+               /* (non-Javadoc)
+                * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
+                */
+               @Override
+               protected void onPostExecute(String result)
+               {
+                       dialog.cancel();
+                       if (result != null)
+                       {
+                               cpList.add(0, result);
+                               if (cpList.size() > maxConnectionPoints)
+                                       cpList.remove(maxConnectionPoints);
+                               refreshList();
+                       }
+               }
+       }
+}
index 6b52f16..3091080 100644 (file)
-/**\r
- * \r
- */\r
-package org.netxms.ui.android.main.activities;\r
-\r
-import java.util.Stack;\r
-\r
-import org.netxms.client.NXCSession;\r
-import org.netxms.client.objects.AbstractObject;\r
-import org.netxms.ui.android.R;\r
-import org.netxms.ui.android.main.adapters.ObjectListAdapter;\r
-\r
-import android.app.ProgressDialog;\r
-import android.content.ComponentName;\r
-import android.content.Intent;\r
-import android.os.AsyncTask;\r
-import android.os.Bundle;\r
-import android.os.IBinder;\r
-import android.util.Log;\r
-import android.view.ContextMenu;\r
-import android.view.ContextMenu.ContextMenuInfo;\r
-import android.view.MenuItem;\r
-import android.view.View;\r
-import android.widget.AdapterView;\r
-import android.widget.ListView;\r
-import android.widget.TextView;\r
-\r
-/**\r
- * Dashboard browser\r
- */\r
-public class DashboardBrowser extends AbstractClientActivity\r
-{\r
-       private static final String TAG = "nxclient/DashboardBrowser";\r
-       private ListView listView;\r
-       private ObjectListAdapter adapter;\r
-       private final long initialParent = 7;\r
-       private AbstractObject currentParent = null;\r
-       private final Stack<AbstractObject> containerPath = new Stack<AbstractObject>();\r
-       private long[] savedPath = null;\r
-       private ProgressDialog dialog;\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)\r
-        */\r
-       @Override\r
-       protected void onCreateStep2(Bundle savedInstanceState)\r
-       {\r
-               dialog = new ProgressDialog(this);\r
-               setContentView(R.layout.node_view);\r
-\r
-               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);\r
-               title.setText(R.string.dashboard_title);\r
-\r
-               // keeps current list of nodes as datasource for listview\r
-               adapter = new ObjectListAdapter(this);\r
-\r
-               listView = (ListView)findViewById(R.id.NodeList);\r
-               listView.setAdapter(adapter);\r
-               listView.setOnItemClickListener(new AdapterView.OnItemClickListener()\r
-               {\r
-                       @Override\r
-                       @SuppressWarnings("rawtypes")\r
-                       public void onItemClick(AdapterView parent, View v, int position, long id)\r
-                       {\r
-                               AbstractObject obj = (AbstractObject)adapter.getItem(position);\r
-                               if (obj.getChildIdList().length > 0)\r
-                               {\r
-                                       containerPath.push(currentParent);\r
-                                       currentParent = obj;\r
-                                       refreshList();\r
-                               }\r
-                               else if (obj.getObjectClass() == AbstractObject.OBJECT_DASHBOARD)\r
-                               {\r
-                                       showDashboard(obj.getObjectId());\r
-                               }\r
-                       }\r
-               });\r
-\r
-               registerForContextMenu(listView);\r
-\r
-               // Restore saved state\r
-               if (savedInstanceState != null)\r
-               {\r
-                       savedPath = savedInstanceState.getLongArray("currentPath");\r
-               }\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)\r
-        */\r
-       @Override\r
-       protected void onSaveInstanceState(Bundle outState)\r
-       {\r
-               outState.putLongArray("currentPath", getFullPathAsId());\r
-               super.onSaveInstanceState(outState);\r
-       }\r
-\r
-       /**\r
-        * @param objectId\r
-        */\r
-       public void showDashboard(long objectId)\r
-       {\r
-               Intent newIntent = new Intent(this, DashboardActivity.class);\r
-               newIntent.putExtra("objectId", objectId);\r
-               startActivity(newIntent);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceConnected(android.content.ComponentName, android.os.IBinder)\r
-        */\r
-       @Override\r
-       public void onServiceConnected(ComponentName name, IBinder binder)\r
-       {\r
-               super.onServiceConnected(name, binder);\r
-\r
-               service.registerDashboardBrowser(this);\r
-\r
-               // Restore to saved path if available\r
-               if ((savedPath != null) && (savedPath.length > 0))\r
-               {\r
-                       for (int i = 0; i < savedPath.length - 1; i++)\r
-                       {\r
-                               AbstractObject object = service.findObjectById(savedPath[i]);\r
-                               if (object == null)\r
-                                       break;\r
-                               containerPath.push(object);\r
-                       }\r
-\r
-                       currentParent = service.findObjectById(savedPath[savedPath.length - 1]);\r
-               }\r
-               savedPath = null;\r
-\r
-               refreshList();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceDisconnected(android.content.ComponentName)\r
-        */\r
-       @Override\r
-       public void onServiceDisconnected(ComponentName name)\r
-       {\r
-               super.onServiceDisconnected(name);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)\r
-        */\r
-       @Override\r
-       public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)\r
-       {\r
-               android.view.MenuInflater inflater = getMenuInflater();\r
-               inflater.inflate(R.menu.node_actions, menu);\r
-\r
-//             AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;\r
-//             AbstractObject object = (AbstractObject)adapter.getItem(info.position);\r
-\r
-               /*\r
-                * if (object instanceof Node) { // add available tools to context menu\r
-                * List<ObjectTool> tools = service.getTools(); if (tools != null) {\r
-                * Iterator<ObjectTool> tl = tools.iterator(); ObjectTool tool;\r
-                * while(tl.hasNext()) { tool = tl.next(); if ((tool.getType() ==\r
-                * ObjectTool.TYPE_ACTION || tool.getType() ==\r
-                * ObjectTool.TYPE_SERVER_COMMAND) &&\r
-                * tool.isApplicableForNode((Node)object)) { menu.add(Menu.NONE,\r
-                * (int)tool.getId(), 0, tool.getDisplayName()); } } } } else\r
-                */\r
-               menu.getItem(0).setVisible(false);\r
-\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)\r
-        */\r
-       @Override\r
-       public boolean onContextItemSelected(MenuItem item)\r
-       {\r
-               // get selected item\r
-//             AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();\r
-//             final GenericObject object = (GenericObject)adapter.getItem(info.position);\r
-\r
-               // process menu selection\r
-               switch (item.getItemId())\r
-               {\r
-                       default:\r
-               }\r
-\r
-               return super.onContextItemSelected(item);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onBackPressed()\r
-        */\r
-       @Override\r
-       public void onBackPressed()\r
-       {\r
-               if (this.currentParent == null)\r
-               {\r
-                       super.onBackPressed();\r
-                       return;\r
-               }\r
-               if (this.currentParent.getObjectId() == this.initialParent)\r
-               {\r
-                       super.onBackPressed();\r
-                       return;\r
-               }\r
-               if (containerPath.empty())\r
-               {\r
-                       super.onBackPressed();\r
-                       return;\r
-               }\r
-\r
-               this.currentParent = containerPath.pop();\r
-               this.refreshList();\r
-               return;\r
-       }\r
-\r
-       /**\r
-        * Refresh dashboards list\r
-        */\r
-       public void refreshList()\r
-       {\r
-               if (currentParent == null)\r
-               {\r
-                       currentParent = service.findObjectById(initialParent);\r
-               }\r
-               if (currentParent == null)\r
-               {\r
-                       // if still null - problem with root node, stop loading\r
-                       return;\r
-               }\r
-\r
-               TextView curPath = (TextView)findViewById(R.id.ScreenTitleSecondary);\r
-               curPath.setText(getFullPath());\r
-               adapter.setNodes(currentParent.getChildsAsArray());\r
-               adapter.notifyDataSetChanged();\r
-               new SyncMissingObjectsTask(currentParent.getObjectId()).execute(new Object[] { currentParent.getChildIdList() });\r
-       }\r
-\r
-       /**\r
-        * Get full path to current position in object tree\r
-        * \r
-        * @return\r
-        */\r
-       private String getFullPath()\r
-       {\r
-               StringBuilder sb = new StringBuilder();\r
-               for (AbstractObject o : containerPath)\r
-               {\r
-                       sb.append('/');\r
-                       sb.append(o.getObjectName());\r
-               }\r
-               if (currentParent != null)\r
-               {\r
-                       sb.append('/');\r
-                       sb.append(currentParent.getObjectName());\r
-               }\r
-               return sb.toString();\r
-       }\r
-\r
-       /**\r
-        * Get full path to current position in object tree, as object identifiers\r
-        * \r
-        * @return\r
-        */\r
-       private long[] getFullPathAsId()\r
-       {\r
-               long[] path = new long[containerPath.size() + ((currentParent != null) ? 1 : 0)];\r
-               int i = 0;\r
-               for (AbstractObject o : containerPath)\r
-                       path[i++] = o.getObjectId();\r
-\r
-               if (currentParent != null)\r
-                       path[i++] = currentParent.getObjectId();\r
-\r
-               return path;\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onDestroy()\r
-        */\r
-       @Override\r
-       protected void onDestroy()\r
-       {\r
-               service.registerDashboardBrowser(null);\r
-               super.onDestroy();\r
-       }\r
-\r
-       /**\r
-        * Update dashboard list, force refresh as necessary\r
-        */\r
-       public void updateDashboardList()\r
-       {\r
-               if (adapter != null)\r
-               {\r
-                       if (currentParent != null)\r
-                       {\r
-                               AbstractObject[] list = currentParent.getChildsAsArray();\r
-                               if (list != null)\r
-                               {\r
-                                       adapter.setNodes(list);\r
-                                       adapter.notifyDataSetChanged();\r
-                                       return;\r
-                               }\r
-                       }\r
-                       refreshList();\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Internal task for synching missing objects\r
-        */\r
-       private class SyncMissingObjectsTask extends AsyncTask<Object, Void, Exception>\r
-       {\r
-               private final long currentRoot;\r
-\r
-               protected SyncMissingObjectsTask(long currentRoot)\r
-               {\r
-                       this.currentRoot = currentRoot;\r
-               }\r
-\r
-               @Override\r
-               protected void onPreExecute()\r
-               {\r
-                       dialog.setMessage(getString(R.string.progress_gathering_data));\r
-                       dialog.setIndeterminate(true);\r
-                       dialog.setCancelable(false);\r
-                       dialog.show();\r
-               }\r
-\r
-               @Override\r
-               protected Exception doInBackground(Object... params)\r
-               {\r
-                       try\r
-                       {\r
-                               service.getSession().syncMissingObjects((long[])params[0], false, NXCSession.OBJECT_SYNC_WAIT);\r
-                       }\r
-                       catch (Exception e)\r
-                       {\r
-                               Log.e(TAG, "Exception while executing service.getSession().syncMissingObjects", e);\r
-                               return e;\r
-                       }\r
-                       return null;\r
-               }\r
-\r
-               @Override\r
-               protected void onPostExecute(Exception result)\r
-               {\r
-                       dialog.cancel();\r
-                       if ((result == null) && (DashboardBrowser.this.currentParent.getObjectId() == currentRoot))\r
-                       {\r
-                               adapter.setNodes(currentParent.getChildsAsArray());\r
-                               adapter.notifyDataSetChanged();\r
-                       }\r
-               }\r
-       }\r
-}\r
+/**
+ * 
+ */
+package org.netxms.ui.android.main.activities;
+
+import java.util.Stack;
+
+import org.netxms.client.NXCSession;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.android.R;
+import org.netxms.ui.android.main.adapters.ObjectListAdapter;
+
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Dashboard browser
+ */
+public class DashboardBrowser extends AbstractClientActivity
+{
+       private static final String TAG = "nxclient/DashboardBrowser";
+       private ListView listView;
+       private ObjectListAdapter adapter;
+       private final long initialParent = 7;
+       private AbstractObject currentParent = null;
+       private final Stack<AbstractObject> containerPath = new Stack<AbstractObject>();
+       private long[] savedPath = null;
+       private ProgressDialog dialog;
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)
+        */
+       @Override
+       protected void onCreateStep2(Bundle savedInstanceState)
+       {
+               dialog = new ProgressDialog(this);
+               setContentView(R.layout.node_view);
+
+               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
+               title.setText(R.string.dashboard_title);
+
+               // keeps current list of nodes as datasource for listview
+               adapter = new ObjectListAdapter(this);
+
+               listView = (ListView)findViewById(R.id.NodeList);
+               listView.setAdapter(adapter);
+               listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
+               {
+                       @Override
+                       @SuppressWarnings("rawtypes")
+                       public void onItemClick(AdapterView parent, View v, int position, long id)
+                       {
+                               AbstractObject obj = (AbstractObject)adapter.getItem(position);
+                               if (obj.getChildIdList().length > 0)
+                               {
+                                       containerPath.push(currentParent);
+                                       currentParent = obj;
+                                       refreshList();
+                               }
+                               else if (obj.getObjectClass() == AbstractObject.OBJECT_DASHBOARD)
+                               {
+                                       showDashboard(obj.getObjectId());
+                               }
+                       }
+               });
+
+               registerForContextMenu(listView);
+
+               // Restore saved state
+               if (savedInstanceState != null)
+               {
+                       savedPath = savedInstanceState.getLongArray("currentPath");
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
+        */
+       @Override
+       protected void onSaveInstanceState(Bundle outState)
+       {
+               outState.putLongArray("currentPath", getFullPathAsId());
+               super.onSaveInstanceState(outState);
+       }
+
+       /**
+        * @param objectId
+        */
+       public void showDashboard(long objectId)
+       {
+               Intent newIntent = new Intent(this, DashboardActivity.class);
+               newIntent.putExtra("objectId", objectId);
+               startActivity(newIntent);
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceConnected(android.content.ComponentName, android.os.IBinder)
+        */
+       @Override
+       public void onServiceConnected(ComponentName name, IBinder binder)
+       {
+               super.onServiceConnected(name, binder);
+
+               service.registerDashboardBrowser(this);
+
+               // Restore to saved path if available
+               if ((savedPath != null) && (savedPath.length > 0))
+               {
+                       for (int i = 0; i < savedPath.length - 1; i++)
+                       {
+                               AbstractObject object = service.findObjectById(savedPath[i]);
+                               if (object == null)
+                                       break;
+                               containerPath.push(object);
+                       }
+
+                       currentParent = service.findObjectById(savedPath[savedPath.length - 1]);
+               }
+               savedPath = null;
+
+               refreshList();
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceDisconnected(android.content.ComponentName)
+        */
+       @Override
+       public void onServiceDisconnected(ComponentName name)
+       {
+               super.onServiceDisconnected(name);
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
+        */
+       @Override
+       public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
+       {
+               android.view.MenuInflater inflater = getMenuInflater();
+               inflater.inflate(R.menu.node_actions, menu);
+
+//             AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
+//             AbstractObject object = (AbstractObject)adapter.getItem(info.position);
+
+               /*
+                * if (object instanceof Node) { // add available tools to context menu
+                * List<ObjectTool> tools = service.getTools(); if (tools != null) {
+                * Iterator<ObjectTool> tl = tools.iterator(); ObjectTool tool;
+                * while(tl.hasNext()) { tool = tl.next(); if ((tool.getType() ==
+                * ObjectTool.TYPE_ACTION || tool.getType() ==
+                * ObjectTool.TYPE_SERVER_COMMAND) &&
+                * tool.isApplicableForNode((Node)object)) { menu.add(Menu.NONE,
+                * (int)tool.getId(), 0, tool.getDisplayName()); } } } } else
+                */
+               menu.getItem(0).setVisible(false);
+
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
+        */
+       @Override
+       public boolean onContextItemSelected(MenuItem item)
+       {
+               // get selected item
+//             AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
+//             final GenericObject object = (GenericObject)adapter.getItem(info.position);
+
+               // process menu selection
+               switch (item.getItemId())
+               {
+                       default:
+               }
+
+               return super.onContextItemSelected(item);
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onBackPressed()
+        */
+       @Override
+       public void onBackPressed()
+       {
+               if (this.currentParent == null)
+               {
+                       super.onBackPressed();
+                       return;
+               }
+               if (this.currentParent.getObjectId() == this.initialParent)
+               {
+                       super.onBackPressed();
+                       return;
+               }
+               if (containerPath.empty())
+               {
+                       super.onBackPressed();
+                       return;
+               }
+
+               this.currentParent = containerPath.pop();
+               this.refreshList();
+               return;
+       }
+
+       /**
+        * Refresh dashboards list
+        */
+       public void refreshList()
+       {
+               if (currentParent == null)
+               {
+                       currentParent = service.findObjectById(initialParent);
+               }
+               if (currentParent == null)
+               {
+                       // if still null - problem with root node, stop loading
+                       return;
+               }
+
+               TextView curPath = (TextView)findViewById(R.id.ScreenTitleSecondary);
+               curPath.setText(getFullPath());
+               adapter.setNodes(currentParent.getChildsAsArray());
+               adapter.notifyDataSetChanged();
+               new SyncMissingObjectsTask(currentParent.getObjectId()).execute(new Object[] { currentParent.getChildIdList() });
+       }
+
+       /**
+        * Get full path to current position in object tree
+        * 
+        * @return
+        */
+       private String getFullPath()
+       {
+               StringBuilder sb = new StringBuilder();
+               for (AbstractObject o : containerPath)
+               {
+                       sb.append('/');
+                       sb.append(o.getObjectName());
+               }
+               if (currentParent != null)
+               {
+                       sb.append('/');
+                       sb.append(currentParent.getObjectName());
+               }
+               return sb.toString();
+       }
+
+       /**
+        * Get full path to current position in object tree, as object identifiers
+        * 
+        * @return
+        */
+       private long[] getFullPathAsId()
+       {
+               long[] path = new long[containerPath.size() + ((currentParent != null) ? 1 : 0)];
+               int i = 0;
+               for (AbstractObject o : containerPath)
+                       path[i++] = o.getObjectId();
+
+               if (currentParent != null)
+                       path[i++] = currentParent.getObjectId();
+
+               return path;
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onDestroy()
+        */
+       @Override
+       protected void onDestroy()
+       {
+               service.registerDashboardBrowser(null);
+               super.onDestroy();
+       }
+
+       /**
+        * Update dashboard list, force refresh as necessary
+        */
+       public void updateDashboardList()
+       {
+               if (adapter != null)
+               {
+                       if (currentParent != null)
+                       {
+                               AbstractObject[] list = currentParent.getChildsAsArray();
+                               if (list != null)
+                               {
+                                       adapter.setNodes(list);
+                                       adapter.notifyDataSetChanged();
+                                       return;
+                               }
+                       }
+                       refreshList();
+               }
+       }
+
+       /**
+        * Internal task for synching missing objects
+        */
+       private class SyncMissingObjectsTask extends AsyncTask<Object, Void, Exception>
+       {
+               private final long currentRoot;
+
+               protected SyncMissingObjectsTask(long currentRoot)
+               {
+                       this.currentRoot = currentRoot;
+               }
+
+               @Override
+               protected void onPreExecute()
+               {
+                       dialog.setMessage(getString(R.string.progress_gathering_data));
+                       dialog.setIndeterminate(true);
+                       dialog.setCancelable(false);
+                       dialog.show();
+               }
+
+               @Override
+               protected Exception doInBackground(Object... params)
+               {
+                       try
+                       {
+                               service.getSession().syncMissingObjects((long[])params[0], false, NXCSession.OBJECT_SYNC_WAIT);
+                       }
+                       catch (Exception e)
+                       {
+                               Log.e(TAG, "Exception while executing service.getSession().syncMissingObjects", e);
+                               return e;
+                       }
+                       return null;
+               }
+
+               @Override
+               protected void onPostExecute(Exception result)
+               {
+                       dialog.cancel();
+                       if ((result == null) && (DashboardBrowser.this.currentParent.getObjectId() == currentRoot))
+                       {
+                               adapter.setNodes(currentParent.getChildsAsArray());
+                               adapter.notifyDataSetChanged();
+                       }
+               }
+       }
+}
index 9a85906..e7797fd 100644 (file)
-/**\r
- * \r
- */\r
-package org.netxms.ui.android.main.activities;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Date;\r
-\r
-import org.netxms.client.datacollection.DciData;\r
-import org.netxms.client.datacollection.DciDataRow;\r
-import org.netxms.client.datacollection.GraphItem;\r
-import org.netxms.client.datacollection.GraphItemStyle;\r
-import org.netxms.ui.android.R;\r
-import org.netxms.ui.android.main.views.ExtendedLineGraphView;\r
-\r
-import android.app.ProgressDialog;\r
-import android.content.ComponentName;\r
-import android.os.AsyncTask;\r
-import android.os.Bundle;\r
-import android.os.IBinder;\r
-import android.util.Log;\r
-import android.widget.LinearLayout;\r
-import android.widget.TextView;\r
-import android.widget.Toast;\r
-\r
-import com.jjoe64.graphview.GraphView;\r
-import com.jjoe64.graphview.GraphView.GraphViewData;\r
-import com.jjoe64.graphview.GraphView.LegendAlign;\r
-import com.jjoe64.graphview.GraphViewSeries;\r
-import com.jjoe64.graphview.GraphViewSeries.GraphViewSeriesStyle;\r
-\r
-/**\r
- * Draw graph activity\r
- * \r
- * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)\r
- * \r
- */\r
-public class DrawGraph extends AbstractClientActivity\r
-{\r
-       private static final String TAG = "nxclient/DrawGraph";\r
-       private GraphView graphView = null;\r
-       private GraphItem[] items = null;\r
-       private GraphItemStyle[] itemStyles = null;\r
-       private int numGraphs = 0;\r
-       private long timeFrom = 0;\r
-       private long timeTo = 0;\r
-       private String graphTitle = "";\r
-       ProgressDialog dialog;\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)\r
-        */\r
-       @Override\r
-       protected void onCreateStep2(Bundle savedInstanceState)\r
-       {\r
-               dialog = new ProgressDialog(this);\r
-               setContentView(R.layout.graphics);\r
-               boolean showLegend = getIntent().getBooleanExtra("showLegend", true);\r
-               numGraphs = getIntent().getIntExtra("numGraphs", 0);\r
-               if (numGraphs > 0)\r
-               {\r
-                       items = new GraphItem[numGraphs];\r
-                       itemStyles = new GraphItemStyle[numGraphs];\r
-                       ArrayList<Integer> nodeIdList = getIntent().getIntegerArrayListExtra("nodeIdList");\r
-                       ArrayList<Integer> dciIdList = getIntent().getIntegerArrayListExtra("dciIdList");\r
-                       ArrayList<Integer> colorList = getIntent().getIntegerArrayListExtra("colorList");\r
-                       ArrayList<Integer> lineWidthList = getIntent().getIntegerArrayListExtra("lineWidthList");\r
-                       ArrayList<String> nameList = getIntent().getStringArrayListExtra("nameList");\r
-                       for (int i = 0; i < numGraphs; i++)\r
-                       {\r
-                               items[i] = new GraphItem();\r
-                               items[i].setNodeId(nodeIdList.get(i));\r
-                               items[i].setDciId(dciIdList.get(i));\r
-                               items[i].setDescription(nameList.get(i));\r
-                               itemStyles[i] = new GraphItemStyle();\r
-                               itemStyles[i].setColor(colorList.get(i) | 0xFF000000);\r
-                               itemStyles[i].setLineWidth(lineWidthList.get(i));\r
-                       }\r
-                       timeFrom = getIntent().getLongExtra("timeFrom", 0);\r
-                       timeTo = getIntent().getLongExtra("timeTo", 0);\r
-                       graphTitle = getIntent().getStringExtra("graphTitle");\r
-               }\r
-               graphView = new ExtendedLineGraphView(this, "");\r
-               graphView.setShowLegend(showLegend);\r
-               graphView.setLegendAlign(LegendAlign.TOP);\r
-               graphView.setLegendWidth(240);\r
-               graphView.setScalable(true);\r
-               graphView.setScrollable(true);\r
-               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);\r
-               title.setText(R.string.graph_title);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceConnected(android.content.ComponentName, android.os.IBinder)\r
-        */\r
-       @Override\r
-       public void onServiceConnected(ComponentName name, IBinder binder)\r
-       {\r
-               super.onServiceConnected(name, binder);\r
-               refreshGraph();\r
-       }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * \r
-        * @see\r
-        * android.content.ServiceConnection#onServiceDisconnected(android.content\r
-        * .ComponentName)\r
-        */\r
-       @Override\r
-       public void onServiceDisconnected(ComponentName name)\r
-       {\r
-               super.onServiceDisconnected(name);\r
-       }\r
-\r
-       /**\r
-        * Refresh node graph\r
-        */\r
-       public void refreshGraph()\r
-       {\r
-               if (graphTitle != null)\r
-               {\r
-                       TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);\r
-                       title.setText(graphTitle);\r
-               }\r
-               new LoadDataTask().execute();\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see android.app.Activity#onDestroy()\r
-        */\r
-       @Override\r
-       protected void onDestroy()\r
-       {\r
-               service.registerNodeBrowser(null);\r
-               super.onDestroy();\r
-       }\r
-\r
-       /**\r
-        * Internal task for loading DCI data\r
-        */\r
-       private class LoadDataTask extends AsyncTask<Object, Void, DciData[]>\r
-       {\r
-               @Override\r
-               protected void onPreExecute()\r
-               {\r
-                       dialog.setMessage(getString(R.string.progress_gathering_data));\r
-                       dialog.setIndeterminate(true);\r
-                       dialog.setCancelable(false);\r
-                       dialog.show();\r
-               }\r
-               @Override\r
-               protected DciData[] doInBackground(Object... params)\r
-               {\r
-                       DciData[] dciData = null;\r
-                       try\r
-                       {\r
-                               if (numGraphs > 0)\r
-                               {\r
-                                       dciData = new DciData[numGraphs];\r
-                                       for (int i = 0; i < numGraphs; i++)\r
-                                       {\r
-                                               dciData[i] = new DciData(0, 0);\r
-                                               dciData[i] = service.getSession().getCollectedData(items[i].getNodeId(), items[i].getDciId(), new Date(timeFrom), new Date(timeTo), 0);\r
-                                       }\r
-                               }\r
-                       }\r
-                       catch (Exception e)\r
-                       {\r
-                               Log.e(TAG, "Exception while executing LoadDataTask.doInBackground", e);\r
-                               dciData = null;\r
-                       }\r
-                       return dciData;\r
-               }\r
-               @Override\r
-               protected void onPostExecute(DciData[] result)\r
-               {\r
-                       int addedSeries = 0;\r
-                       if (result != null)\r
-                       {\r
-                               double start = 0;\r
-                               double end = 0;\r
-                               for (int i = 0; i < result.length; i++)\r
-                               {\r
-                                       DciDataRow[] dciDataRow = result[i].getValues();\r
-                                       if (dciDataRow.length > 0)\r
-                                       {\r
-                                               GraphViewData[] gwData = new GraphViewData[dciDataRow.length];\r
-                                               // dciData are reversed!\r
-                                               for (int j = dciDataRow.length - 1, k = 0; j >= 0; j--, k++)\r
-                                                       gwData[k] = new GraphViewData(dciDataRow[j].getTimestamp().getTime(), dciDataRow[j].getValueAsDouble());\r
-                                               GraphViewSeries gwSeries = new GraphViewSeries(\r
-                                                               items[i].getDescription(),\r
-                                                               new GraphViewSeriesStyle(itemStyles[i].getColor(), itemStyles[i].getLineWidth()),\r
-                                                               gwData);\r
-                                               graphView.addSeries(gwSeries);\r
-                                               addedSeries++;\r
-                                               start = dciDataRow[dciDataRow.length - 1].getTimestamp().getTime();\r
-                                               end = dciDataRow[0].getTimestamp().getTime();\r
-                                       }\r
-                               }\r
-                               if (addedSeries == 0) // Add an empty series when getting no data\r
-                               {\r
-                                       GraphViewData[] gwData = new GraphViewData[] { new GraphViewData(0, 0) };\r
-                                       GraphViewSeries gwSeries = new GraphViewSeries("", new GraphViewSeriesStyle(0xFFFFFF, 4), gwData);\r
-                                       graphView.addSeries(gwSeries);\r
-                               }\r
-                               LinearLayout layout = (LinearLayout)findViewById(R.id.graphics);\r
-                               if (layout != null)\r
-                               {\r
-                                       graphView.setViewPort(start, end - start + 1); // Start showing full graph\r
-                                       layout.addView(graphView);\r
-                               }\r
-                       }\r
-                       dialog.cancel();\r
-                       if (addedSeries == 0)\r
-                               Toast.makeText(getApplicationContext(), getString(R.string.notify_no_data), Toast.LENGTH_SHORT).show();\r
-               }\r
-       }\r
-}\r
+/**
+ * 
+ */
+package org.netxms.ui.android.main.activities;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.netxms.client.datacollection.DciData;
+import org.netxms.client.datacollection.DciDataRow;
+import org.netxms.client.datacollection.GraphItem;
+import org.netxms.client.datacollection.GraphItemStyle;
+import org.netxms.ui.android.R;
+import org.netxms.ui.android.main.views.ExtendedLineGraphView;
+
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.jjoe64.graphview.GraphView;
+import com.jjoe64.graphview.GraphView.GraphViewData;
+import com.jjoe64.graphview.GraphView.LegendAlign;
+import com.jjoe64.graphview.GraphViewSeries;
+import com.jjoe64.graphview.GraphViewSeries.GraphViewSeriesStyle;
+
+/**
+ * Draw graph activity
+ * 
+ * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
+ * 
+ */
+public class DrawGraph extends AbstractClientActivity
+{
+       private static final String TAG = "nxclient/DrawGraph";
+       private GraphView graphView = null;
+       private GraphItem[] items = null;
+       private GraphItemStyle[] itemStyles = null;
+       private int numGraphs = 0;
+       private long timeFrom = 0;
+       private long timeTo = 0;
+       private String graphTitle = "";
+       ProgressDialog dialog;
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)
+        */
+       @Override
+       protected void onCreateStep2(Bundle savedInstanceState)
+       {
+               dialog = new ProgressDialog(this);
+               setContentView(R.layout.graphics);
+               boolean showLegend = getIntent().getBooleanExtra("showLegend", true);
+               numGraphs = getIntent().getIntExtra("numGraphs", 0);
+               if (numGraphs > 0)
+               {
+                       items = new GraphItem[numGraphs];
+                       itemStyles = new GraphItemStyle[numGraphs];
+                       ArrayList<Integer> nodeIdList = getIntent().getIntegerArrayListExtra("nodeIdList");
+                       ArrayList<Integer> dciIdList = getIntent().getIntegerArrayListExtra("dciIdList");
+                       ArrayList<Integer> colorList = getIntent().getIntegerArrayListExtra("colorList");
+                       ArrayList<Integer> lineWidthList = getIntent().getIntegerArrayListExtra("lineWidthList");
+                       ArrayList<String> nameList = getIntent().getStringArrayListExtra("nameList");
+                       for (int i = 0; i < numGraphs; i++)
+                       {
+                               items[i] = new GraphItem();
+                               items[i].setNodeId(nodeIdList.get(i));
+                               items[i].setDciId(dciIdList.get(i));
+                               items[i].setDescription(nameList.get(i));
+                               itemStyles[i] = new GraphItemStyle();
+                               itemStyles[i].setColor(colorList.get(i) | 0xFF000000);
+                               itemStyles[i].setLineWidth(lineWidthList.get(i));
+                       }
+                       timeFrom = getIntent().getLongExtra("timeFrom", 0);
+                       timeTo = getIntent().getLongExtra("timeTo", 0);
+                       graphTitle = getIntent().getStringExtra("graphTitle");
+               }
+               graphView = new ExtendedLineGraphView(this, "");
+               graphView.setShowLegend(showLegend);
+               graphView.setLegendAlign(LegendAlign.TOP);
+               graphView.setLegendWidth(240);
+               graphView.setScalable(true);
+               graphView.setScrollable(true);
+               TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
+               title.setText(R.string.graph_title);
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onServiceConnected(android.content.ComponentName, android.os.IBinder)
+        */
+       @Override
+       public void onServiceConnected(ComponentName name, IBinder binder)
+       {
+               super.onServiceConnected(name, binder);
+               refreshGraph();
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * android.content.ServiceConnection#onServiceDisconnected(android.content
+        * .ComponentName)
+        */
+       @Override
+       public void onServiceDisconnected(ComponentName name)
+       {
+               super.onServiceDisconnected(name);
+       }
+
+       /**
+        * Refresh node graph
+        */
+       public void refreshGraph()
+       {
+               if (graphTitle != null)
+               {
+                       TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
+                       title.setText(graphTitle);
+               }
+               new LoadDataTask().execute();
+       }
+
+       /* (non-Javadoc)
+        * @see android.app.Activity#onDestroy()
+        */
+       @Override
+       protected void onDestroy()
+       {
+               service.registerNodeBrowser(null);
+               super.onDestroy();
+       }
+
+       /**
+        * Internal task for loading DCI data
+        */
+       private class LoadDataTask extends AsyncTask<Object, Void, DciData[]>
+       {
+               @Override
+               protected void onPreExecute()
+               {
+                       dialog.setMessage(getString(R.string.progress_gathering_data));
+                       dialog.setIndeterminate(true);
+                       dialog.setCancelable(false);
+                       dialog.show();
+               }
+               @Override
+               protected DciData[] doInBackground(Object... params)
+               {
+                       DciData[] dciData = null;
+                       try
+                       {
+                               if (numGraphs > 0)
+                               {
+                                       dciData = new DciData[numGraphs];
+                                       for (int i = 0; i < numGraphs; i++)
+                                       {
+                                               dciData[i] = new DciData(0, 0);
+                                               dciData[i] = service.getSession().getCollectedData(items[i].getNodeId(), items[i].getDciId(), new Date(timeFrom), new Date(timeTo), 0);
+                                       }
+                               }
+                       }
+                       catch (Exception e)
+                       {
+                               Log.e(TAG, "Exception while executing LoadDataTask.doInBackground", e);
+                               dciData = null;
+                       }
+                       return dciData;
+               }
+               @Override
+               protected void onPostExecute(DciData[] result)
+               {
+                       int addedSeries = 0;
+                       if (result != null)
+                       {
+                               double start = 0;
+                               double end = 0;
+                               for (int i = 0; i < result.length; i++)
+                               {
+                                       DciDataRow[] dciDataRow = result[i].getValues();
+                                       if (dciDataRow.length > 0)
+                                       {
+                                               GraphViewData[] gwData = new GraphViewData[dciDataRow.length];
+                                               // dciData are reversed!
+                                               for (int j = dciDataRow.length - 1, k = 0; j >= 0; j--, k++)
+                                                       gwData[k] = new GraphViewData(dciDataRow[j].getTimestamp().getTime(), dciDataRow[j].getValueAsDouble());
+                    &nb