From 6d48729efb4bd1ac34bba003a8a4313a70a02cce Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 16 Feb 2013 16:55:44 +1300 Subject: Handle clicks on markers (fix #8) --- src/org/traccar/web/client/Application.java | 40 ++++++++++++++- .../web/client/controller/ArchiveController.java | 4 ++ .../web/client/controller/DeviceController.java | 4 ++ .../web/client/controller/MapController.java | 50 +++++++++++++++++-- .../web/client/controller/StateController.java | 57 +++++++++++++++++++++ src/org/traccar/web/client/model/StateItem.java | 31 ++++++++++++ .../web/client/model/StateItemProperties.java | 17 +++++++ src/org/traccar/web/client/model/StateReader.java | 43 ++++++++++++++++ .../traccar/web/client/view/ApplicationView.java | 6 ++- .../traccar/web/client/view/ApplicationView.ui.xml | 18 ++++++- src/org/traccar/web/client/view/ArchiveView.java | 4 ++ src/org/traccar/web/client/view/DeviceView.java | 4 ++ .../web/client/view/MapPositionRenderer.java | 42 +++++++++++++++- src/org/traccar/web/client/view/MapView.java | 48 ++++++++++++++---- src/org/traccar/web/client/view/StateView.java | 58 ++++++++++++++++++++++ src/org/traccar/web/client/view/StateView.ui.xml | 22 ++++++++ .../traccar/web/server/model/DataServiceImpl.java | 2 + src/org/traccar/web/shared/model/Device.java | 2 +- 18 files changed, 431 insertions(+), 21 deletions(-) create mode 100644 src/org/traccar/web/client/controller/StateController.java create mode 100644 src/org/traccar/web/client/model/StateItem.java create mode 100644 src/org/traccar/web/client/model/StateItemProperties.java create mode 100644 src/org/traccar/web/client/model/StateReader.java create mode 100644 src/org/traccar/web/client/view/StateView.java create mode 100644 src/org/traccar/web/client/view/StateView.ui.xml (limited to 'src/org/traccar/web') diff --git a/src/org/traccar/web/client/Application.java b/src/org/traccar/web/client/Application.java index c802995..07e2586 100644 --- a/src/org/traccar/web/client/Application.java +++ b/src/org/traccar/web/client/Application.java @@ -20,6 +20,7 @@ import java.util.logging.Logger; import org.traccar.web.client.controller.ArchiveController; import org.traccar.web.client.controller.DeviceController; import org.traccar.web.client.controller.MapController; +import org.traccar.web.client.controller.StateController; import org.traccar.web.client.model.BaseStoreHandlers; import org.traccar.web.client.model.DataService; import org.traccar.web.client.model.DataServiceAsync; @@ -48,6 +49,7 @@ public class Application { } private final DeviceController deviceController; + private final StateController stateController; private final MapController mapController; private final ArchiveController archiveController; @@ -56,31 +58,65 @@ public class Application { public Application() { deviceController = new DeviceController(deviceHandler); deviceController.getDeviceStore().addStoreHandlers(deviceStoreHandler); - mapController = new MapController(); + stateController = new StateController(); + mapController = new MapController(mapHandler); archiveController = new ArchiveController(archiveHanlder, deviceController.getDeviceStore()); archiveController.getPositionStore().addStoreHandlers(archiveStoreHandler); view = new ApplicationView( - deviceController.getView(), mapController.getView(), archiveController.getView()); + deviceController.getView(), stateController.getView(), mapController.getView(), archiveController.getView()); } public void run() { RootPanel.get().add(view); deviceController.run(); + stateController.run(); mapController.run(); archiveController.run(); } private DeviceController.DeviceHandler deviceHandler = new DeviceController.DeviceHandler() { + private Device selected; + @Override public void onSelected(Device device) { + if (selected != null) { + mapController.unregisterPositionUpdate(selected); + } + if (device != null) { + mapController.registerPositionUpdate(device, positionUpdateHandler); + } + selected = device; mapController.selectDevice(device); } }; + private MapController.PositionUpdateHandler positionUpdateHandler = new MapController.PositionUpdateHandler() { + + @Override + public void onUpdate(Position position) { + stateController.showState(position); + } + + }; + + private MapController.MapHandler mapHandler = new MapController.MapHandler() { + + @Override + public void onDeviceSelected(Device device) { + deviceController.selectDevice(device); + } + + @Override + public void onArchivePositionSelected(Position position) { + archiveController.selectPosition(position); + } + + }; + private ArchiveController.ArchiveHandler archiveHanlder = new ArchiveController.ArchiveHandler() { @Override diff --git a/src/org/traccar/web/client/controller/ArchiveController.java b/src/org/traccar/web/client/controller/ArchiveController.java index 1146252..553e674 100644 --- a/src/org/traccar/web/client/controller/ArchiveController.java +++ b/src/org/traccar/web/client/controller/ArchiveController.java @@ -87,4 +87,8 @@ public class ArchiveController implements ContentController, ArchiveView.Archive positionStore.clear(); } + public void selectPosition(Position position) { + archiveView.selectPosition(position); + } + } diff --git a/src/org/traccar/web/client/controller/DeviceController.java b/src/org/traccar/web/client/controller/DeviceController.java index 24ed2a1..75aa76f 100644 --- a/src/org/traccar/web/client/controller/DeviceController.java +++ b/src/org/traccar/web/client/controller/DeviceController.java @@ -124,4 +124,8 @@ public class DeviceController implements ContentController, DeviceView.DeviceHan dialog.show(); } + public void selectDevice(Device device) { + deviceView.selectDevice(device); + } + } diff --git a/src/org/traccar/web/client/controller/MapController.java b/src/org/traccar/web/client/controller/MapController.java index 44094f7..13c736f 100644 --- a/src/org/traccar/web/client/controller/MapController.java +++ b/src/org/traccar/web/client/controller/MapController.java @@ -17,8 +17,10 @@ package org.traccar.web.client.controller; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.traccar.web.client.Application; import org.traccar.web.client.view.MapView; @@ -29,14 +31,22 @@ import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sencha.gxt.widget.core.client.ContentPanel; -public class MapController implements ContentController { +public class MapController implements ContentController, MapView.MapHandler { private static final int UPDATE_INTERVAL = 15000; + public interface MapHandler { + public void onDeviceSelected(Device device); + public void onArchivePositionSelected(Position position); + } + + private MapHandler mapHandler; + private MapView mapView; - public MapController() { - mapView = new MapView(); + public MapController(MapHandler mapHandler) { + this.mapHandler = mapHandler; + mapView = new MapView(this); } @Override @@ -57,12 +67,20 @@ public class MapController implements ContentController { update(); } + private Map latestPositionMap = new HashMap(); + public void update() { updateTimer.cancel(); Application.getDataService().getLatestPositions(new AsyncCallback>() { @Override public void onSuccess(List result) { mapView.showLatestPositions(result); + for (Position position : result) { + latestPositionMap.put(position.getDevice().getId(), position); + } + for (Map.Entry entry : positionUpdateMap.entrySet()) { + entry.getValue().onUpdate(latestPositionMap.get(entry.getKey())); + } updateTimer.schedule(UPDATE_INTERVAL); } @Override @@ -91,4 +109,30 @@ public class MapController implements ContentController { mapView.selectArchivePosition(position); } + public interface PositionUpdateHandler { + public void onUpdate(Position position); + } + + private Map positionUpdateMap = new HashMap(); + + + public void registerPositionUpdate(Device device, PositionUpdateHandler handler) { + positionUpdateMap.put(device.getId(), handler); + handler.onUpdate(latestPositionMap.get(device.getId())); + } + + public void unregisterPositionUpdate(Device device) { + positionUpdateMap.remove(device.getId()); + } + + @Override + public void onPositionSelected(Position position) { + mapHandler.onDeviceSelected(position.getDevice()); + } + + @Override + public void onArchivePositionSelected(Position position) { + mapHandler.onArchivePositionSelected(position); + } + } diff --git a/src/org/traccar/web/client/controller/StateController.java b/src/org/traccar/web/client/controller/StateController.java new file mode 100644 index 0000000..a19bec8 --- /dev/null +++ b/src/org/traccar/web/client/controller/StateController.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web.client.controller; + +import org.traccar.web.client.model.StateItem; +import org.traccar.web.client.model.StateItemProperties; +import org.traccar.web.client.model.StateReader; +import org.traccar.web.client.view.StateView; +import org.traccar.web.shared.model.Position; + +import com.google.gwt.core.client.GWT; +import com.sencha.gxt.data.shared.ListStore; +import com.sencha.gxt.widget.core.client.ContentPanel; + +public class StateController implements ContentController { + + private ListStore stateStore; + + private StateView stateView; + + public StateController() { + StateItemProperties stateItemProperties = GWT.create(StateItemProperties.class); + stateStore = new ListStore(stateItemProperties.id()); + stateView = new StateView(stateStore); + } + + @Override + public ContentPanel getView() { + return stateView.getView(); + } + + @Override + public void run() { + } + + public void showState(Position position) { + if (position != null) { + stateStore.replaceAll(StateReader.getState(position)); + } else { + stateStore.clear(); + } + } + +} diff --git a/src/org/traccar/web/client/model/StateItem.java b/src/org/traccar/web/client/model/StateItem.java new file mode 100644 index 0000000..651d2a2 --- /dev/null +++ b/src/org/traccar/web/client/model/StateItem.java @@ -0,0 +1,31 @@ +package org.traccar.web.client.model; + + +public class StateItem { + + public StateItem(String name, String value) { + this.name = name; + this.value = value; + } + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/org/traccar/web/client/model/StateItemProperties.java b/src/org/traccar/web/client/model/StateItemProperties.java new file mode 100644 index 0000000..e3700a5 --- /dev/null +++ b/src/org/traccar/web/client/model/StateItemProperties.java @@ -0,0 +1,17 @@ +package org.traccar.web.client.model; + +import com.google.gwt.editor.client.Editor.Path; +import com.sencha.gxt.core.client.ValueProvider; +import com.sencha.gxt.data.shared.ModelKeyProvider; +import com.sencha.gxt.data.shared.PropertyAccess; + +public interface StateItemProperties extends PropertyAccess { + + @Path("name") + ModelKeyProvider id(); + + ValueProvider name(); + + ValueProvider value(); + +} diff --git a/src/org/traccar/web/client/model/StateReader.java b/src/org/traccar/web/client/model/StateReader.java new file mode 100644 index 0000000..a354917 --- /dev/null +++ b/src/org/traccar/web/client/model/StateReader.java @@ -0,0 +1,43 @@ +package org.traccar.web.client.model; + +import java.util.LinkedList; +import java.util.List; + +import org.traccar.web.shared.model.Position; +import org.traccar.web.shared.model.XmlParser; + +import com.google.gwt.i18n.client.DateTimeFormat; + +public class StateReader { + + private static String toString(Object object) { + if (object != null) { + return object.toString(); + } + return null; + } + + public static List getState(Position position) { + List state = new LinkedList(); + + state.add(new StateItem("valid", toString(position.getValid()))); + state.add(new StateItem("time", DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss").format(position.getTime()))); + state.add(new StateItem("latitude", toString(position.getLatitude()))); + state.add(new StateItem("longitude", toString(position.getLongitude()))); + state.add(new StateItem("altitude", toString(position.getAltitude()))); + state.add(new StateItem("speed", toString(position.getSpeed()))); + state.add(new StateItem("course", toString(position.getCourse()))); + state.add(new StateItem("power", toString(position.getPower()))); + state.add(new StateItem("address", position.getAddress())); + + String other = position.getOther(); + if (other != null) { + for (String key : XmlParser.enumerateElements(other)) { + state.add(new StateItem(key, XmlParser.getElement(other, key))); + } + } + + return state; + } + +} diff --git a/src/org/traccar/web/client/view/ApplicationView.java b/src/org/traccar/web/client/view/ApplicationView.java index f4c42e5..dfa5335 100644 --- a/src/org/traccar/web/client/view/ApplicationView.java +++ b/src/org/traccar/web/client/view/ApplicationView.java @@ -32,14 +32,18 @@ public class ApplicationView extends Composite { @UiField(provided = true) ContentPanel devicePanel; + @UiField(provided = true) + ContentPanel statePanel; + @UiField(provided = true) ContentPanel mapPanel; @UiField(provided = true) ContentPanel archivePanel; - public ApplicationView(ContentPanel deviceView, ContentPanel mapView, ContentPanel archiveView) { + public ApplicationView(ContentPanel deviceView, ContentPanel stateView, ContentPanel mapView, ContentPanel archiveView) { devicePanel = deviceView; + statePanel = stateView; mapPanel = mapView; archivePanel = archiveView; initWidget(uiBinder.createAndBindUi(this)); diff --git a/src/org/traccar/web/client/view/ApplicationView.ui.xml b/src/org/traccar/web/client/view/ApplicationView.ui.xml index 2faae1e..0b05ab9 100644 --- a/src/org/traccar/web/client/view/ApplicationView.ui.xml +++ b/src/org/traccar/web/client/view/ApplicationView.ui.xml @@ -5,7 +5,10 @@ xmlns:container="urn:import:com.sencha.gxt.widget.core.client.container" xmlns:gxt="urn:import:com.sencha.gxt.widget.core.client"> - + + + + @@ -25,10 +28,21 @@ + + + + - + + + + + + + + diff --git a/src/org/traccar/web/client/view/ArchiveView.java b/src/org/traccar/web/client/view/ArchiveView.java index 67b5750..32e569e 100644 --- a/src/org/traccar/web/client/view/ArchiveView.java +++ b/src/org/traccar/web/client/view/ArchiveView.java @@ -181,4 +181,8 @@ public class ArchiveView implements SelectionChangedEvent.SelectionChangedHandle }; + public void selectPosition(Position position) { + grid.getSelectionModel().select(positionStore.findModel(position), false); + } + } diff --git a/src/org/traccar/web/client/view/DeviceView.java b/src/org/traccar/web/client/view/DeviceView.java index 5f6441b..c3f3f7a 100644 --- a/src/org/traccar/web/client/view/DeviceView.java +++ b/src/org/traccar/web/client/view/DeviceView.java @@ -134,4 +134,8 @@ public class DeviceView implements SelectionChangedEvent.SelectionChangedHandler }); } + public void selectDevice(Device device) { + grid.getSelectionModel().select(deviceStore.findModel(device), false); + } + } diff --git a/src/org/traccar/web/client/view/MapPositionRenderer.java b/src/org/traccar/web/client/view/MapPositionRenderer.java index f1bf57c..15ba082 100644 --- a/src/org/traccar/web/client/view/MapPositionRenderer.java +++ b/src/org/traccar/web/client/view/MapPositionRenderer.java @@ -21,6 +21,8 @@ import java.util.Map; import org.gwtopenmaps.openlayers.client.Icon; import org.gwtopenmaps.openlayers.client.Marker; +import org.gwtopenmaps.openlayers.client.event.EventHandler; +import org.gwtopenmaps.openlayers.client.event.EventObject; import org.gwtopenmaps.openlayers.client.feature.VectorFeature; import org.gwtopenmaps.openlayers.client.geometry.LineString; import org.gwtopenmaps.openlayers.client.geometry.Point; @@ -29,9 +31,16 @@ import org.gwtopenmaps.openlayers.client.layer.Vector; import org.traccar.web.shared.model.Device; import org.traccar.web.shared.model.Position; +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.ui.RootPanel; + public class MapPositionRenderer { + public interface SelectHandler { + public void onSelected(Position position); + } + private final MapView mapView; private final MarkerIconFactory.IconType iconType; @@ -43,14 +52,41 @@ public class MapPositionRenderer { return mapView.getMarkerLayer(); } - public MapPositionRenderer(MapView mapView, MarkerIconFactory.IconType iconType) { + private SelectHandler selectHandler; + + public MapPositionRenderer(MapView mapView, MarkerIconFactory.IconType iconType, SelectHandler selectHandler) { this.mapView = mapView; this.iconType = iconType; + this.selectHandler = selectHandler; + } + + private void addSelectEvent(Marker marker, final Position position) { + if (selectHandler != null) { + marker.getEvents().register("click", marker, new EventHandler() { + @Override + public void onHandle(EventObject eventObject) { + selectHandler.onSelected(position); + } + }); + marker.getEvents().register("mouseover", marker, new EventHandler() { + @Override + public void onHandle(EventObject eventObject) { + RootPanel.get().getElement().getStyle().setCursor(Style.Cursor.SE_RESIZE); + } + }); + marker.getEvents().register("mouseout", marker, new EventHandler() { + @Override + public void onHandle(EventObject eventObject) { + RootPanel.get().getElement().getStyle().setCursor(Style.Cursor.AUTO); + } + }); + } } private void changeMarkerIcon(Long positionId, Icon icon) { Marker oldMarker = markerMap.get(positionId); Marker newMarker = new Marker(oldMarker.getLonLat(), icon); + addSelectEvent(newMarker, positionMap.get(positionId)); markerMap.put(positionId, newMarker); getMarkerLayer().addMarker(newMarker); getMarkerLayer().removeMarker(oldMarker); @@ -58,6 +94,7 @@ public class MapPositionRenderer { private Map markerMap = new HashMap(); // Position.id -> Marker private Map deviceMap = new HashMap(); // Device.id -> Position.id + private Map positionMap = new HashMap(); // Position.id -> Position private Long selectedPositionId; private Long selectedDeviceId; @@ -68,6 +105,7 @@ public class MapPositionRenderer { } markerMap.clear(); deviceMap.clear(); + positionMap.clear(); for (Position position : positions) { Marker marker = new Marker( @@ -75,6 +113,8 @@ public class MapPositionRenderer { MarkerIconFactory.getIcon(iconType, false)); markerMap.put(position.getId(), marker); deviceMap.put(position.getDevice().getId(), position.getId()); + positionMap.put(position.getId(), position); + addSelectEvent(marker, position); getMarkerLayer().addMarker(marker); } diff --git a/src/org/traccar/web/client/view/MapView.java b/src/org/traccar/web/client/view/MapView.java index f57ec65..e54381d 100644 --- a/src/org/traccar/web/client/view/MapView.java +++ b/src/org/traccar/web/client/view/MapView.java @@ -47,6 +47,13 @@ import com.sencha.gxt.widget.core.client.ContentPanel; public class MapView { + public interface MapHandler { + public void onPositionSelected(Position position); + public void onArchivePositionSelected(Position position); + } + + private MapHandler mapHandler; + private ContentPanel contentPanel; public ContentPanel getView() { @@ -81,33 +88,34 @@ public class MapView { point.transform(new Projection("EPSG:4326"), new Projection(map.getProjection())); return point; } - + private void initMapLayers(Map map) { map.addLayer(OSM.Mapnik("OpenStreetMap")); - + GoogleV3Options gHybridOptions = new GoogleV3Options(); gHybridOptions.setType(GoogleV3MapType.G_HYBRID_MAP); map.addLayer(new GoogleV3("Google Hybrid", gHybridOptions)); - + GoogleV3Options gNormalOptions = new GoogleV3Options(); gNormalOptions.setType(GoogleV3MapType.G_NORMAL_MAP); map.addLayer(new GoogleV3("Google Normal", gNormalOptions)); - + GoogleV3Options gSatelliteOptions = new GoogleV3Options(); gSatelliteOptions.setType(GoogleV3MapType.G_SATELLITE_MAP); map.addLayer(new GoogleV3("Google Satellite", gSatelliteOptions)); - + GoogleV3Options gTerrainOptions = new GoogleV3Options(); gTerrainOptions.setType(GoogleV3MapType.G_TERRAIN_MAP); - map.addLayer(new GoogleV3("Google Terrain", gTerrainOptions)); - + map.addLayer(new GoogleV3("Google Terrain", gTerrainOptions)); + final String bingKey = "AseEs0DLJhLlTNoxbNXu7DGsnnH4UoWuGue7-irwKkE3fffaClwc9q_Mr6AyHY8F"; map.addLayer(new Bing(new BingOptions("Bing Road", bingKey, BingType.ROAD))); map.addLayer(new Bing(new BingOptions("Bing Hybrid", bingKey, BingType.HYBRID))); map.addLayer(new Bing(new BingOptions("Bing Aerial", bingKey, BingType.AERIAL))); } - public MapView() { + public MapView(MapHandler mapHandler) { + this.mapHandler = mapHandler; contentPanel = new ContentPanel(); contentPanel.setHeadingText("Map"); @@ -124,7 +132,7 @@ public class MapView { markerLayer = new Markers("Markers", markersOptions); initMapLayers(map); - + map.addLayer(vectorLayer); map.addLayer(markerLayer); @@ -147,8 +155,8 @@ public class MapView { } }); - latestPositionRenderer = new MapPositionRenderer(this, MarkerIconFactory.IconType.iconLatest); - archivePositionRenderer = new MapPositionRenderer(this, MarkerIconFactory.IconType.iconArchive); + latestPositionRenderer = new MapPositionRenderer(this, MarkerIconFactory.IconType.iconLatest, latestPositionSelectHandler); + archivePositionRenderer = new MapPositionRenderer(this, MarkerIconFactory.IconType.iconArchive, archivePositionSelectHandler); } private final MapPositionRenderer latestPositionRenderer; @@ -172,4 +180,22 @@ public class MapView { archivePositionRenderer.selectPosition(position, true); } + private MapPositionRenderer.SelectHandler latestPositionSelectHandler = new MapPositionRenderer.SelectHandler() { + + @Override + public void onSelected(Position position) { + mapHandler.onPositionSelected(position); + } + + }; + + private MapPositionRenderer.SelectHandler archivePositionSelectHandler = new MapPositionRenderer.SelectHandler() { + + @Override + public void onSelected(Position position) { + mapHandler.onArchivePositionSelected(position); + } + + }; + } diff --git a/src/org/traccar/web/client/view/StateView.java b/src/org/traccar/web/client/view/StateView.java new file mode 100644 index 0000000..700d55a --- /dev/null +++ b/src/org/traccar/web/client/view/StateView.java @@ -0,0 +1,58 @@ +package org.traccar.web.client.view; + +import java.util.LinkedList; +import java.util.List; + +import org.traccar.web.client.model.StateItem; +import org.traccar.web.client.model.StateItemProperties; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Widget; +import com.sencha.gxt.data.shared.ListStore; +import com.sencha.gxt.widget.core.client.ContentPanel; +import com.sencha.gxt.widget.core.client.grid.ColumnConfig; +import com.sencha.gxt.widget.core.client.grid.ColumnModel; +import com.sencha.gxt.widget.core.client.grid.Grid; + +public class StateView { + + private static StateViewUiBinder uiBinder = GWT.create(StateViewUiBinder.class); + + interface StateViewUiBinder extends UiBinder { + } + + @UiField + ContentPanel contentPanel; + + public ContentPanel getView() { + return contentPanel; + } + + @UiField(provided = true) + ColumnModel columnModel; + + @UiField(provided = true) + ListStore stateStore; + + @UiField + Grid grid; + + public StateView(ListStore stateStore) { + this.stateStore = stateStore; + + StateItemProperties stateItemProperties = GWT.create(StateItemProperties.class); + + List> columnConfigList = new LinkedList>(); + columnConfigList.add(new ColumnConfig(stateItemProperties.name(), 0, "Attribute")); + columnConfigList.add(new ColumnConfig(stateItemProperties.value(), 0, "Value")); + columnModel = new ColumnModel(columnConfigList); + + uiBinder.createAndBindUi(this); + + grid.getSelectionModel().setLocked(true); + } + +} + diff --git a/src/org/traccar/web/client/view/StateView.ui.xml b/src/org/traccar/web/client/view/StateView.ui.xml new file mode 100644 index 0000000..2a7c2ac --- /dev/null +++ b/src/org/traccar/web/client/view/StateView.ui.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/src/org/traccar/web/server/model/DataServiceImpl.java b/src/org/traccar/web/server/model/DataServiceImpl.java index e48fa73..287a568 100644 --- a/src/org/traccar/web/server/model/DataServiceImpl.java +++ b/src/org/traccar/web/server/model/DataServiceImpl.java @@ -152,6 +152,7 @@ public class DataServiceImpl extends RemoteServiceServlet implements DataService try { entityManager.persist(device); user.getDevices().add(device); + entityManager.merge(user); entityManager.getTransaction().commit(); return device; } catch (RuntimeException e) { @@ -191,6 +192,7 @@ public class DataServiceImpl extends RemoteServiceServlet implements DataService device = entityManager.merge(device); user.getDevices().remove(device); entityManager.remove(device); + entityManager.merge(user); entityManager.getTransaction().commit(); return device; } catch (RuntimeException e) { diff --git a/src/org/traccar/web/shared/model/Device.java b/src/org/traccar/web/shared/model/Device.java index ede3c5f..324bad8 100644 --- a/src/org/traccar/web/shared/model/Device.java +++ b/src/org/traccar/web/shared/model/Device.java @@ -49,7 +49,7 @@ public class Device implements Serializable { return id; } - @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) + @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Position latestPosition; public Position getLatestPosition() { -- cgit v1.2.3