package com.kedzie.vbox.machine; import pl.polidea.treeview.AbstractTreeViewAdapter; import pl.polidea.treeview.InMemoryTreeStateManager; import pl.polidea.treeview.TreeBuilder; import pl.polidea.treeview.TreeNodeInfo; import pl.polidea.treeview.TreeStateManager; import pl.polidea.treeview.TreeViewList; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ListView; import android.widget.TextView; import com.actionbarsherlock.app.SherlockFragment; import com.actionbarsherlock.view.MenuInflater; import com.kedzie.vbox.R; import com.kedzie.vbox.VBoxApplication; import com.kedzie.vbox.VMAction; import com.kedzie.vbox.api.IConsole; import com.kedzie.vbox.api.IMachine; import com.kedzie.vbox.api.IProgress; import com.kedzie.vbox.api.ISnapshot; import com.kedzie.vbox.api.ISnapshotDeletedEvent; import com.kedzie.vbox.api.ISnapshotTakenEvent; import com.kedzie.vbox.api.jaxb.VBoxEventType; import com.kedzie.vbox.app.Utils; import com.kedzie.vbox.event.EventIntentService; import com.kedzie.vbox.soap.VBoxSvc; import com.kedzie.vbox.task.ActionBarTask; import com.kedzie.vbox.task.MachineTask; /** * * @apiviz.stereotype fragment */ public class SnapshotFragment extends SherlockFragment { /** * Load complete snapshot tree. */ class LoadSnapshotsTask extends ActionBarTask<IMachine, ISnapshot> { public LoadSnapshotsTask(VBoxSvc vmgr) { super(getSherlockActivity(), vmgr); } @Override protected ISnapshot work(IMachine... params) throws Exception { ISnapshot root = null; synchronized(params[0]) { params[0].clearCacheNamed("getCurrentSnapshot"); root = params[0].getCurrentSnapshot(); } if(root==null) return null; while(root.getParent()!=null) root = root.getParent(); cache(root); return root; } /** * Recursively cache all the children of a {@link ISnapshot} * @param s the {@link ISnapshot} */ protected void cache(ISnapshot s) { s.getName(); s.getDescription(); for(ISnapshot child : s.getChildren()) cache(child); } @Override protected void onSuccess(ISnapshot result) { _root=result; _stateManager = new InMemoryTreeStateManager<ISnapshot>(); _treeBuilder = new TreeBuilder<ISnapshot>(_stateManager); _treeView.setAdapter(new SnapshotTreeAdapter(getActivity(), _stateManager, 10)); if(result!=null) populate(null, result); } } class HandleDeletedEventTask extends ActionBarTask<ISnapshotDeletedEvent, ISnapshot> { public HandleDeletedEventTask(VBoxSvc vmgr) { super(getSherlockActivity(), vmgr); } @Override protected ISnapshot work(ISnapshotDeletedEvent... params) throws Exception { ISnapshot snapshot = findSnapshot(_root, params[0].getSnapshotId()); snapshot.getName(); snapshot.getDescription(); if(snapshot.getParent()!=null) snapshot.getParent().getName(); return snapshot; } @Override protected void onSuccess(ISnapshot result) { _stateManager.removeNodeRecursively(result); if(result.equals(_root)) { _root=null; } } } class HandleAddedEventTask extends ActionBarTask<ISnapshotTakenEvent, ISnapshot> { public HandleAddedEventTask(VBoxSvc vmgr) { super(getSherlockActivity(), vmgr); } @Override protected ISnapshot work(ISnapshotTakenEvent... params) throws Exception { _machine.clearCacheNamed("getCurrentSnapshot"); ISnapshot snapshot = _machine.getCurrentSnapshot(); snapshot.getName(); snapshot.getDescription(); if(snapshot.getParent()!=null) snapshot.getParent().getName(); return snapshot; } @Override protected void onSuccess(ISnapshot result) { if(result.getParent()==null) { _root=result; _treeBuilder.sequentiallyAddNextNode(result, 0); } else { ISnapshot parent = _stateManager.getNodeInfo(result.getParent()).getId(); _treeBuilder.addRelation(parent, result); } } } /** * Snapshot tree node */ class SnapshotTreeAdapter extends AbstractTreeViewAdapter<ISnapshot> { public SnapshotTreeAdapter(Activity activity, TreeStateManager<ISnapshot> treeStateManager, int numberOfLevels) { super(activity, treeStateManager, numberOfLevels); } @Override public long getItemId(int position) { return position; } @Override public View getNewChildView(TreeNodeInfo<ISnapshot> treeNodeInfo) { View v = LayoutInflater.from(getActivity()).inflate(android.R.layout.simple_list_item_1, null); TextView text1 = (TextView)v.findViewById(android.R.id.text1); v.setTag(text1); updateView(v, treeNodeInfo); return v; } @Override public View updateView(View view, TreeNodeInfo<ISnapshot> treeNodeInfo) { TextView text1 = (TextView)view.getTag(); text1.setText(treeNodeInfo.getId().getName()); text1.setCompoundDrawablesWithIntrinsicBounds(getApp().getDrawable(VMAction.RESTORE_SNAPSHOT) , 0, 0, 0); return view; } } protected VBoxSvc _vmgr; protected IMachine _machine; protected TreeViewList _treeView; private ISnapshot _root; protected TreeStateManager<ISnapshot> _stateManager; protected TreeBuilder<ISnapshot> _treeBuilder; private LocalBroadcastManager _lbm; private BroadcastReceiver _receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(VBoxEventType.ON_SNAPSHOT_TAKEN.name())){ ISnapshotTakenEvent event = intent.getParcelableExtra(EventIntentService.BUNDLE_EVENT); Utils.toastShort(getActivity(), "Snapshot event: %1$s", intent.getAction()); new HandleAddedEventTask(_vmgr).execute(event); // refresh(); } else if(intent.getAction().equals(VBoxEventType.ON_SNAPSHOT_DELETED.name())){ ISnapshotDeletedEvent event = intent.getParcelableExtra(EventIntentService.BUNDLE_EVENT); Utils.toastShort(getActivity(), "Snapshot event: %1$s", intent.getAction()); new HandleDeletedEventTask(_vmgr).execute(event); // refresh(); } } }; private ISnapshot findSnapshot(ISnapshot current, String id) { if(current.getId().equals(id)) return current; for(ISnapshot child : current.getChildren()) { ISnapshot found = findSnapshot(child, id); if(found!=null) return found; } return null; } /** * Recursively populate the tree structure * @param parent * @param child */ protected void populate(ISnapshot parent, ISnapshot snapshot) { if(parent==null) _treeBuilder.sequentiallyAddNextNode(snapshot, 0); else _treeBuilder.addRelation(parent, snapshot); for(ISnapshot child : snapshot.getChildren()) populate(snapshot, child); } /** * Refresh the snapshot tree */ private void refresh() { _stateManager.clear(); _stateManager = new InMemoryTreeStateManager<ISnapshot>(); _treeBuilder = new TreeBuilder<ISnapshot>(_stateManager); _treeView.setAdapter(new SnapshotTreeAdapter(getActivity(), _stateManager, 10)); new LoadSnapshotsTask( _vmgr).execute(_machine); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable("manager", _stateManager); outState.putParcelable("root", _root); } @SuppressWarnings("unchecked") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); _vmgr = getArguments().getParcelable(VBoxSvc.BUNDLE); _machine = getArguments().getParcelable(IMachine.BUNDLE); _machine = _vmgr.getProxy(IMachine.class, _machine.getIdRef()); if(savedInstanceState!=null) { _stateManager = (TreeStateManager<ISnapshot>)savedInstanceState.getSerializable("manager"); _root = savedInstanceState.getParcelable("root"); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.snapshot_tree, container, false); _treeView = (TreeViewList)view.findViewById(R.id.mainTreeView); _treeView.setChoiceMode(ListView.CHOICE_MODE_NONE); registerForContextMenu(_treeView); return view; } @Override public void onStart() { super.onStart(); _lbm = LocalBroadcastManager.getInstance(getActivity()); _lbm.registerReceiver(_receiver, Utils.createIntentFilter( VBoxEventType.ON_SNAPSHOT_DELETED.name(), VBoxEventType.ON_SNAPSHOT_TAKEN.name())); if(_stateManager==null) new LoadSnapshotsTask(_vmgr).execute(_machine); else { _treeBuilder = new TreeBuilder<ISnapshot>(_stateManager); _treeView.setAdapter(new SnapshotTreeAdapter(getActivity(), _stateManager, 10)); } } @Override public void onStop() { _lbm.unregisterReceiver(_receiver); super.onStop(); } private VBoxApplication getApp() { return (VBoxApplication)getActivity().getApplication(); } @Override public void onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.snapshot_actions, menu); } @Override public boolean onOptionsItemSelected(com.actionbarsherlock.view.MenuItem item) { switch(item.getItemId()) { case R.id.option_menu_add: Utils.showDialog(getFragmentManager(), "snapshotDialog", TakeSnapshotFragment.getInstance(_vmgr, _machine, null) ); return true; case R.id.option_menu_refresh: refresh(); return false; } return false; } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { menu.add(Menu.NONE, R.id.context_menu_restore_snapshot, Menu.NONE, R.string.action_restore_snapshot); menu.add(Menu.NONE, R.id.context_menu_delete_snapshot, Menu.NONE, R.string.action_delete_snapshot); menu.add(Menu.NONE, R.id.context_menu_details_snapshot, Menu.NONE, R.string.action_edit_snapshot); } private SnapshotTreeAdapter getTreeAdapter() { return (SnapshotTreeAdapter)_treeView.getAdapter(); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo(); TreeNodeInfo<ISnapshot> nodeinfo; switch (item. getItemId()) { case R.id.context_menu_details_snapshot: nodeinfo = getTreeAdapter().getTreeNodeInfo(info.position); Utils.showDialog(getFragmentManager(), "snapshotDialog", TakeSnapshotFragment.getInstance(_vmgr, _machine, nodeinfo.getId()) ); return true; case R.id.context_menu_delete_snapshot: nodeinfo = getTreeAdapter().getTreeNodeInfo(info.position); new MachineTask<ISnapshot, Void>(getSherlockActivity(), _vmgr, R.string.progress_deleting_snapshot, false, _machine) { protected IProgress workWithProgress(IMachine m, IConsole console, ISnapshot...s) throws Exception { return console.deleteSnapshot(s[0].getId()); } }.execute(nodeinfo.getId()); return true; case R.id.context_menu_restore_snapshot: nodeinfo = getTreeAdapter().getTreeNodeInfo(info.position); new MachineTask<ISnapshot, Void>(getSherlockActivity(), _vmgr, R.string.progress_restore_snapshot, false, _machine) { protected IProgress workWithProgress(IMachine m, IConsole console, ISnapshot...s) throws Exception { return console.restoreSnapshot(s[0]); } }.execute(nodeinfo.getId()); return true; } return false; } }