a0e9fdee038d1f6d90ac4daa2588b9c1618ca738
[public/netxms.git] / android / src / console / src / org / netxms / ui / android / main / activities / NodeBrowser.java
1 /**
2 *
3 */
4 package org.netxms.ui.android.main.activities;
5
6 import java.util.ArrayList;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Stack;
10
11 import org.netxms.base.GeoLocation;
12 import org.netxms.client.NXCSession;
13 import org.netxms.client.constants.NodePoller;
14 import org.netxms.client.objects.AbstractObject;
15 import org.netxms.client.objects.GenericObject;
16 import org.netxms.client.objects.Node;
17 import org.netxms.client.objecttools.ObjectTool;
18 import org.netxms.ui.android.NXApplication;
19 import org.netxms.ui.android.R;
20 import org.netxms.ui.android.main.adapters.ObjectListAdapter;
21 import org.netxms.ui.android.main.fragments.AlarmBrowserFragment;
22 import org.netxms.ui.android.main.fragments.NodeInfoFragment;
23
24 import android.app.AlertDialog;
25 import android.app.ProgressDialog;
26 import android.content.ActivityNotFoundException;
27 import android.content.ComponentName;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnClickListener;
30 import android.content.Intent;
31 import android.net.Uri;
32 import android.os.AsyncTask;
33 import android.os.Bundle;
34 import android.os.IBinder;
35 import android.util.Log;
36 import android.view.ContextMenu;
37 import android.view.ContextMenu.ContextMenuInfo;
38 import android.view.Menu;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.widget.AdapterView;
42 import android.widget.AdapterView.AdapterContextMenuInfo;
43 import android.widget.ListView;
44 import android.widget.TextView;
45 import android.widget.Toast;
46
47 /**
48 * Node browser
49 *
50 * @author Victor Kirhenshtein
51 * @author Marco Incalcaterra (marco.incalcaterra@thinksoft.it)
52 *
53 */
54
55 public class NodeBrowser extends AbstractClientActivity
56 {
57 private static final String TAG = "nxclient/NodeBrowser";
58 private ListView listView;
59 private ObjectListAdapter adapter;
60 private long initialParent;
61 private AbstractObject currentParent = null;
62 private final Stack<AbstractObject> containerPath = new Stack<AbstractObject>();
63 private long[] savedPath = null;
64 private AbstractObject selectedObject = null;
65 private ProgressDialog dialog;
66
67 /* (non-Javadoc)
68 * @see org.netxms.ui.android.main.activities.AbstractClientActivity#onCreateStep2(android.os.Bundle)
69 */
70 @Override
71 protected void onCreateStep2(Bundle savedInstanceState)
72 {
73 dialog = new ProgressDialog(this);
74 setContentView(R.layout.node_view);
75
76 TextView title = (TextView)findViewById(R.id.ScreenTitlePrimary);
77 title.setText(R.string.nodes_title);
78
79 initialParent = getIntent().getIntExtra("parentId", GenericObject.SERVICEROOT);
80
81 // keeps current list of nodes as datasource for listview
82 adapter = new ObjectListAdapter(this);
83
84 listView = (ListView)findViewById(R.id.NodeList);
85 listView.setAdapter(adapter);
86 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
87 @Override
88 @SuppressWarnings("rawtypes")
89 public void onItemClick(AdapterView parent, View v, int position, long id)
90 {
91 AbstractObject obj = (AbstractObject)adapter.getItem(position);
92 if ((obj.getObjectClass() == AbstractObject.OBJECT_CONTAINER) ||
93 (obj.getObjectClass() == AbstractObject.OBJECT_SUBNET) ||
94 (obj.getObjectClass() == AbstractObject.OBJECT_CLUSTER) ||
95 (obj.getObjectClass() == AbstractObject.OBJECT_ZONE))
96 {
97 containerPath.push(currentParent);
98 currentParent = obj;
99 refreshList();
100 }
101 else if (obj.getObjectClass() == AbstractObject.OBJECT_NODE || obj.getObjectClass() == AbstractObject.OBJECT_MOBILEDEVICE)
102 {
103 showNodeInfo(obj.getObjectId());
104 }
105 }
106 });
107
108 registerForContextMenu(listView);
109
110 // Restore saved state
111 if (savedInstanceState != null)
112 savedPath = savedInstanceState.getLongArray("currentPath");
113 }
114
115 /* (non-Javadoc)
116 * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
117 */
118 @Override
119 protected void onSaveInstanceState(Bundle outState)
120 {
121 savedPath = getFullPathAsId();
122 outState.putLongArray("currentPath", savedPath);
123 super.onSaveInstanceState(outState);
124 }
125
126 /* (non-Javadoc)
127 * @see android.app.Activity#onResume()
128 */
129 @Override
130 protected void onResume()
131 {
132 super.onResume();
133 NXApplication.activityResumed();
134 if (service != null)
135 {
136 service.reconnect(false);
137 rescanSavedPath();
138 }
139 }
140
141 /**
142 * @param objectId
143 */
144 public void showNodeInfo(long objectId)
145 {
146 Intent newIntent = new Intent(this, NodeInfoFragment.class);
147 newIntent.putExtra("objectId", objectId);
148 startActivity(newIntent);
149 }
150
151 /*
152 * (non-Javadoc)
153 *
154 * @see android.content.ServiceConnection#onServiceConnected(android.content.
155 * ComponentName, android.os.IBinder)
156 */
157 @Override
158 public void onServiceConnected(ComponentName name, IBinder binder)
159 {
160 super.onServiceConnected(name, binder);
161
162 service.registerNodeBrowser(this);
163 rescanSavedPath();
164 }
165
166 /* (non-Javadoc)
167 * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
168 */
169 @Override
170 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
171 {
172 android.view.MenuInflater inflater = getMenuInflater();
173 inflater.inflate(R.menu.node_actions, menu);
174
175 AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
176 selectedObject = (AbstractObject)adapter.getItem(info.position);
177
178 GeoLocation gl = selectedObject.getGeolocation();
179 if ((gl == null) || (gl.getType() == GeoLocation.UNSET))
180 {
181 hideMenuItem(menu, R.id.navigate_to);
182 }
183
184 if (selectedObject instanceof Node)
185 {
186 // add available tools to context menu
187 List<ObjectTool> tools = service.getTools();
188 if (tools != null)
189 {
190 Iterator<ObjectTool> tl = tools.iterator();
191 ObjectTool tool;
192 while (tl.hasNext())
193 {
194 tool = tl.next();
195 if ((tool.getType() == ObjectTool.TYPE_ACTION || tool.getType() == ObjectTool.TYPE_SERVER_COMMAND) &&
196 tool.isApplicableForNode((Node)selectedObject))
197 {
198 menu.add(Menu.NONE, (int)tool.getId(), 0, tool.getDisplayName());
199 }
200 }
201 }
202 }
203 else
204 {
205 hideMenuItem(menu, R.id.find_switch_port);
206 hideMenuItem(menu, R.id.poll);
207 }
208 }
209
210 /**
211 * @param menu
212 * @param id
213 */
214 private void hideMenuItem(ContextMenu menu, int id)
215 {
216 MenuItem item = menu.findItem(id);
217 if (item != null)
218 item.setVisible(false);
219 }
220
221 /* (non-Javadoc)
222 * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
223 */
224 @Override
225 public boolean onContextItemSelected(MenuItem item)
226 {
227 if (selectedObject == null)
228 return super.onContextItemSelected(item);
229
230 switch(item.getItemId())
231 {
232 case R.id.find_switch_port:
233 Intent fspIntent = new Intent(this, ConnectionPointBrowser.class);
234 fspIntent.putExtra("nodeId", (int)selectedObject.getObjectId());
235 startActivity(fspIntent);
236 break;
237 case R.id.view_alarms:
238 new SyncMissingChildsTask().execute(new Integer[] { (int)selectedObject.getObjectId() });
239 break;
240 case R.id.unmanage:
241 service.setObjectMgmtState(selectedObject.getObjectId(), false);
242 refreshList();
243 break;
244 case R.id.manage:
245 service.setObjectMgmtState(selectedObject.getObjectId(), true);
246 refreshList();
247 break;
248 case R.id.poll_status:
249 Intent psIntent = new Intent(this, NodePollerActivity.class);
250 psIntent.putExtra("nodeId", (int)selectedObject.getObjectId());
251 psIntent.putExtra("pollType", NodePoller.STATUS_POLL);
252 startActivity(psIntent);
253 break;
254 case R.id.poll_configuration:
255 Intent pcIntent = new Intent(this, NodePollerActivity.class);
256 pcIntent.putExtra("nodeId", (int)selectedObject.getObjectId());
257 pcIntent.putExtra("pollType", NodePoller.CONFIGURATION_POLL);
258 startActivity(pcIntent);
259 break;
260 case R.id.poll_topology:
261 Intent ptIntent = new Intent(this, NodePollerActivity.class);
262 ptIntent.putExtra("nodeId", (int)selectedObject.getObjectId());
263 ptIntent.putExtra("pollType", NodePoller.TOPOLOGY_POLL);
264 startActivity(ptIntent);
265 break;
266 case R.id.poll_interfaces:
267 Intent piIntent = new Intent(this, NodePollerActivity.class);
268 piIntent.putExtra("nodeId", (int)selectedObject.getObjectId());
269 piIntent.putExtra("pollType", NodePoller.INTERFACE_POLL);
270 startActivity(piIntent);
271 break;
272 case R.id.navigate_to:
273 GeoLocation gl = selectedObject.getGeolocation();
274 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=" + gl.getLatitude() + ","
275 + gl.getLongitude()));
276 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
277 try
278 {
279 startActivity(intent);
280 }
281 catch(ActivityNotFoundException e)
282 {
283 Toast.makeText(getApplicationContext(), "Navigation unavailable", Toast.LENGTH_LONG);
284 }
285 break;
286 default:
287 // if we didn't match static menu, check if it was some of tools
288 List<ObjectTool> tools = service.getTools();
289 if (tools != null)
290 {
291 for (final ObjectTool tool : tools)
292 {
293 if ((int)tool.getId() == item.getItemId())
294 {
295 if ((tool.getFlags() & ObjectTool.ASK_CONFIRMATION) != 0)
296 {
297 String message = tool.getConfirmationText()
298 .replaceAll("%OBJECT_NAME%", selectedObject.getObjectName())
299 .replaceAll("%OBJECT_IP_ADDR%", selectedObject.getPrimaryIP().getHostAddress());
300 new AlertDialog.Builder(this)
301 .setIcon(android.R.drawable.ic_dialog_alert)
302 .setTitle(R.string.confirm_tool_execution)
303 .setMessage(message)
304 .setCancelable(true)
305 .setPositiveButton(R.string.yes, new OnClickListener()
306 {
307 @Override
308 public void onClick(DialogInterface dialog, int which)
309 {
310 service.executeAction(selectedObject.getObjectId(), tool.getData());
311 }
312 })
313 .setNegativeButton(R.string.no, null)
314 .show();
315 break;
316 }
317 service.executeAction(selectedObject.getObjectId(), tool.getData());
318 break;
319 }
320 }
321 }
322 break;
323 }
324
325 return super.onContextItemSelected(item);
326 }
327
328 /* (non-Javadoc)
329 * @see android.app.Activity#onBackPressed()
330 */
331 @Override
332 public void onBackPressed()
333 {
334 if (this.currentParent == null)
335 {
336 super.onBackPressed();
337 return;
338 }
339 if (this.currentParent.getObjectId() == this.initialParent)
340 {
341 super.onBackPressed();
342 return;
343 }
344 if (containerPath.empty())
345 {
346 super.onBackPressed();
347 return;
348 }
349
350 this.currentParent = containerPath.pop();
351 this.refreshList();
352 return;
353 }
354
355 /**
356 * Refresh node list, force reload from server
357 */
358 public void refreshList()
359 {
360 if (currentParent == null)
361 {
362 currentParent = service.findObjectById(initialParent);
363 }
364 if (currentParent == null)
365 {
366 // if still null - problem with root node, stop loading
367 return;
368 }
369
370 TextView curPath = (TextView)findViewById(R.id.ScreenTitleSecondary);
371 curPath.setText(getFullPath());
372 new SyncMissingObjectsTask(currentParent.getObjectId()).execute(new Object[] { currentParent.getChildIdList() });
373 }
374
375 /**
376 * Rescan saved path
377 */
378 private void rescanSavedPath()
379 {
380 // Restore to saved path if available
381 if ((savedPath != null) && (savedPath.length > 0))
382 {
383 containerPath.clear();
384 for (int i = 0; i < savedPath.length - 1; i++)
385 {
386 AbstractObject object = service.findObjectById(savedPath[i]);
387 if (object == null)
388 break;
389 containerPath.push(object);
390 Log.i(TAG, "object.getObjectId(): " + object.getObjectId());
391 }
392 currentParent = service.findObjectById(savedPath[savedPath.length - 1]);
393 savedPath = null;
394 }
395 refreshList();
396 }
397
398 /**
399 * Get full path to current position in object tree
400 *
401 * @return
402 */
403 private String getFullPath()
404 {
405 StringBuilder sb = new StringBuilder();
406 for (AbstractObject o : containerPath)
407 {
408 sb.append('/');
409 sb.append(o.getObjectName());
410 }
411 if (currentParent != null)
412 {
413 sb.append('/');
414 sb.append(currentParent.getObjectName());
415 }
416 return sb.toString();
417 }
418
419 /**
420 * Get full path to current position in object tree, as object identifiers
421 *
422 * @return
423 */
424 private long[] getFullPathAsId()
425 {
426 long[] path = new long[containerPath.size() + ((currentParent != null) ? 1 : 0)];
427 int i = 0;
428 for (AbstractObject o : containerPath)
429 path[i++] = o.getObjectId();
430
431 if (currentParent != null)
432 path[i++] = currentParent.getObjectId();
433
434 return path;
435 }
436
437 /*
438 * (non-Javadoc)
439 *
440 * @see android.app.Activity#onDestroy()
441 */
442 @Override
443 protected void onDestroy()
444 {
445 service.registerNodeBrowser(null);
446 super.onDestroy();
447 }
448
449 /**
450 * Update node list, force refresh as necessary
451 */
452 public void updateNodeList()
453 {
454 if (adapter != null)
455 {
456 if (currentParent != null)
457 {
458 AbstractObject[] list = currentParent.getChildsAsArray();
459 if (list != null)
460 {
461 adapter.setNodes(list);
462 adapter.notifyDataSetChanged();
463 return;
464 }
465 }
466 refreshList();
467 }
468 }
469
470 /**
471 * @param nodeIdList
472 */
473 private void viewAlarms(ArrayList<Integer> nodeIdList)
474 {
475 Intent newIntent = new Intent(this, AlarmBrowserFragment.class);
476 newIntent.putIntegerArrayListExtra("nodeIdList", nodeIdList);
477 startActivity(newIntent);
478 }
479
480 /**
481 * Internal task for synching missing objects
482 */
483 private class SyncMissingObjectsTask extends AsyncTask<Object, Void, AbstractObject[]>
484 {
485 private final long currentRoot;
486
487 protected SyncMissingObjectsTask(long currentRoot)
488 {
489 this.currentRoot = currentRoot;
490 }
491
492 @Override
493 protected void onPreExecute()
494 {
495 dialog.setMessage(getString(R.string.progress_gathering_data));
496 dialog.setIndeterminate(true);
497 dialog.setCancelable(false);
498 dialog.show();
499 }
500
501 @Override
502 protected AbstractObject[] doInBackground(Object... params)
503 {
504 try
505 {
506 service.getSession().syncMissingObjects((long[])params[0], false, NXCSession.OBJECT_SYNC_WAIT);
507 return currentParent.getChildsAsArray();
508 }
509 catch (Exception e)
510 {
511 Log.e(TAG, "Exception while executing service.getSession().syncMissingObjects in SyncMissingObjectsTask", e);
512 }
513 return null;
514 }
515
516 @Override
517 protected void onPostExecute(AbstractObject[] result)
518 {
519 dialog.cancel();
520 if ((result != null) && (currentParent.getObjectId() == currentRoot))
521 {
522 adapter.setNodes(result);
523 adapter.notifyDataSetChanged();
524 }
525 }
526 }
527
528 /**
529 * Internal task for synching missing objects
530 */
531 private class SyncMissingChildsTask extends AsyncTask<Integer, Void, Integer>
532 {
533 private final ArrayList<Integer> childIdList;
534
535 protected SyncMissingChildsTask()
536 {
537 childIdList = new ArrayList<Integer>();
538 }
539
540 @Override
541 protected void onPreExecute()
542 {
543 dialog.setMessage(getString(R.string.progress_gathering_data));
544 dialog.setIndeterminate(true);
545 dialog.setCancelable(false);
546 dialog.show();
547 }
548
549 protected void getChildsList(long[] list)
550 {
551 for (int i = 0; i < list.length; i++)
552 {
553 childIdList.add((int)list[i]);
554 AbstractObject obj = service.findObjectById(list[i]);
555 if (obj != null && (obj.getObjectClass() == AbstractObject.OBJECT_CONTAINER ||
556 obj.getObjectClass() == AbstractObject.OBJECT_CLUSTER))
557 {
558 try
559 {
560 service.getSession().syncMissingObjects(obj.getChildIdList(), false, NXCSession.OBJECT_SYNC_WAIT);
561 getChildsList(obj.getChildIdList());
562 }
563 catch (Exception e)
564 {
565 Log.e(TAG, "Exception while executing service.getSession().syncMissingObjects in SyncMissingChildsTask", e);
566 }
567 }
568 }
569 }
570
571 @Override
572 protected Integer doInBackground(Integer... params)
573 {
574 long[] list = new long[params.length];
575 for (int i = 0; i < params.length; i++)
576 list[i] = params[i].longValue();
577 getChildsList(list);
578 return 0;
579 }
580
581 @Override
582 protected void onPostExecute(Integer result)
583 {
584 dialog.cancel();
585 viewAlarms(childIdList);
586 }
587 }
588 }