diff --git a/org.envirocar.app.test/src/org/envirocar/app/test/dao/CacheDAOTest.java b/org.envirocar.app.test/src/org/envirocar/app/test/dao/CacheDAOTest.java index 78be8e091..ac9648e7f 100644 --- a/org.envirocar.app.test/src/org/envirocar/app/test/dao/CacheDAOTest.java +++ b/org.envirocar.app.test/src/org/envirocar/app/test/dao/CacheDAOTest.java @@ -57,6 +57,14 @@ protected void prepareCache(File baseFolder, String asset, String assetFile) thr fw.close(); isr.close(); } + + protected void clearCache(File baseFolder, String assetFile) throws IOException { + File f = new File(baseFolder, assetFile); + + if (f != null && f.exists()) { + f.delete(); + } + } public DAOProvider getDAOProvider() throws IOException { MockupCacheDirectoryProvider mockupDir = getMockupDir(); diff --git a/org.envirocar.app.test/src/org/envirocar/app/test/dao/SensorDAOTest.java b/org.envirocar.app.test/src/org/envirocar/app/test/dao/SensorDAOTest.java index 3af425a2a..4d9338b67 100644 --- a/org.envirocar.app.test/src/org/envirocar/app/test/dao/SensorDAOTest.java +++ b/org.envirocar.app.test/src/org/envirocar/app/test/dao/SensorDAOTest.java @@ -33,7 +33,7 @@ public class SensorDAOTest extends CacheDAOTest { - public void testGetAllSensorsCached() throws IOException, SensorRetrievalException { + public void testGetAllSensorsCachedLegacy() throws IOException, SensorRetrievalException { DAOProvider prov = getDAOProvider(); prepareCache(getMockupDir().getBaseFolder(), "sensors_mockup.json", CacheSensorDAO.CAR_CACHE_FILE_NAME); @@ -43,4 +43,18 @@ public void testGetAllSensorsCached() throws IOException, SensorRetrievalExcepti Assert.assertTrue("Expected 1 sensor. Got "+sensors.size(), sensors.size() == 1); } + public void testGetAllSensorsCached() throws IOException, SensorRetrievalException { + DAOProvider prov = getDAOProvider(); + + prepareCache(getMockupDir().getBaseFolder(), "sensors_mockup.json", CacheSensorDAO.CAR_CACHE_FILE_NAME+1); + prepareCache(getMockupDir().getBaseFolder(), "sensors_mockup.json", CacheSensorDAO.CAR_CACHE_FILE_NAME+2); + + SensorDAO dao = prov.getSensorDAO(); + List sensors = dao.getAllSensors(); + Assert.assertTrue("Expected 2 sensors. Got "+sensors.size(), sensors.size() == 2); + + clearCache(getMockupDir().getBaseFolder(), CacheSensorDAO.CAR_CACHE_FILE_NAME+1); + clearCache(getMockupDir().getBaseFolder(), CacheSensorDAO.CAR_CACHE_FILE_NAME+2); + } + } diff --git a/org.envirocar.app/AndroidManifest.xml b/org.envirocar.app/AndroidManifest.xml index aaec152b3..5cf72d4ed 100644 --- a/org.envirocar.app/AndroidManifest.xml +++ b/org.envirocar.app/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="0.9.0"> sortByManufacturer(List sensors) { + final Collator collator = Collator.getInstance(Locale.ENGLISH); + + Collections.sort(sensors, new Comparator() { + @Override + public int compare(Car lhs, Car rhs) { + if (lhs.getManufacturer().equals(rhs.getManufacturer())) { + return collator.compare(lhs.getModel(), rhs.getModel()); + } + else { + return collator.compare(lhs.getManufacturer(), rhs.getManufacturer()); + } + } + }); + + return sensors; + } + + } } diff --git a/org.envirocar.app/src/org/envirocar/app/dao/cache/AbstractCacheDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/cache/AbstractCacheDAO.java index ffbff361d..67769e8d1 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/cache/AbstractCacheDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/cache/AbstractCacheDAO.java @@ -52,6 +52,14 @@ public JSONObject readCache(String cachedFile) throws IOException, JSONException throw new IOException(String.format("Could not read file %s", cachedFile)); } + public boolean cacheFileExists(String cachedFile) { + File directory = cacheDirectoryProvider.getBaseFolder(); + + File f = new File(directory, cachedFile); + + return f != null && f.isFile(); + } + protected void storeCache(String cacheFileName, String content) throws IOException { File file = new File(this.cacheDirectoryProvider.getBaseFolder(), cacheFileName); Util.saveContentsToFile(content, file); diff --git a/org.envirocar.app/src/org/envirocar/app/dao/cache/CacheSensorDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/cache/CacheSensorDAO.java index ca0622496..da408b14b 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/cache/CacheSensorDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/cache/CacheSensorDAO.java @@ -30,6 +30,7 @@ import org.envirocar.app.logging.Logger; import org.envirocar.app.model.Car; import org.json.JSONException; +import org.json.JSONObject; public class CacheSensorDAO extends AbstractCacheDAO implements SensorDAO { @@ -43,7 +44,32 @@ public CacheSensorDAO(CacheDirectoryProvider cacheDirectoryProvider) { @Override public List getAllSensors() throws SensorRetrievalException { try { - return Car.fromJsonList(readCache(CAR_CACHE_FILE_NAME)); + List result = null; + + int c = 1; + while (true) { + if (cacheFileExists(CAR_CACHE_FILE_NAME+c)) { + if (result == null) { + result = Car.fromJsonList(readCache(CAR_CACHE_FILE_NAME+c)); + } + else { + result.addAll(Car.fromJsonList(readCache(CAR_CACHE_FILE_NAME+c))); + } + } + else { + break; + } + c++; + } + + /* + * fallback for old cache states + */ + if (result == null) { + result = Car.fromJsonList(readCache(CAR_CACHE_FILE_NAME)); + } + + return SensorDAOUtil.sortByManufacturer(result); } catch (IOException e) { logger.warn(e.getMessage()); throw new SensorRetrievalException(e); @@ -53,14 +79,17 @@ public List getAllSensors() throws SensorRetrievalException { } } - public void storeAllSensors(String content) throws IOException { - storeCache(CAR_CACHE_FILE_NAME, content); - } - @Override public String saveSensor(Car car) throws NotConnectedException { throw new NotConnectedException("CacheSensorDAO does not support saving."); } + public void storeAllSensors(List parentObject) throws IOException { + int c = 1; + for (JSONObject jsonObject : parentObject) { + storeCache(CAR_CACHE_FILE_NAME+(c++), jsonObject.toString()); + } + } + } diff --git a/org.envirocar.app/src/org/envirocar/app/dao/remote/BaseRemoteDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/remote/BaseRemoteDAO.java index 692600de8..519355e36 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/remote/BaseRemoteDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/remote/BaseRemoteDAO.java @@ -23,6 +23,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.zip.GZIPInputStream; import org.apache.http.HttpEntity; @@ -37,6 +40,7 @@ import org.envirocar.app.dao.exception.NotConnectedException; import org.envirocar.app.dao.exception.ResourceConflictException; import org.envirocar.app.dao.exception.UnauthorizedException; +import org.envirocar.app.exception.ServerException; import org.envirocar.app.model.User; import org.envirocar.app.network.HTTPClient; import org.envirocar.app.util.Util; @@ -121,7 +125,7 @@ private HttpResponse executeHttpRequest(HttpUriRequest request) throws NotConnec * @throws IOException * @throws JSONException */ - protected JSONObject readRemoteResouce(String remoteRestResource) throws NotConnectedException, UnauthorizedException, IOException, JSONException { + protected JSONObject readRemoteResource(String remoteRestResource) throws NotConnectedException, UnauthorizedException, IOException, JSONException { HttpGet get = new HttpGet(ECApplication.BASE_URL+remoteRestResource); InputStream response = retrieveHttpContent(get); String content = Util.consumeInputStream(response).toString(); @@ -130,6 +134,46 @@ protected JSONObject readRemoteResouce(String remoteRestResource) throws NotConn return parentObject; } + /** + * Reads a remote REST resource + * + * @param remoteRestResource the sub resource + * @param complete if the complete resource (all pages) should be read + * @return the remote resource encoded as a {@link JSONObject} + * @throws NotConnectedException + * @throws UnauthorizedException + * @throws IOException + * @throws JSONException + */ + protected List readRemoteResource(String remoteRestResource, boolean complete) throws NotConnectedException, UnauthorizedException, IOException, JSONException { + if (!complete) { + return Collections.singletonList(readRemoteResource(remoteRestResource)); + } + + HttpGet get = new HttpGet(ECApplication.BASE_URL+remoteRestResource+"?limit=100"); + HttpResponse response = executeContentRequest(get); + + Integer count; + try { + count = Util.resolveResourceCount(response); + } catch (ServerException e) { + throw new IOException(e); + } + + if (count > 1) { + List result = new ArrayList(count); + + for (int i = 1; i <= count; i++) { + result.add(readRemoteResource(remoteRestResource+"?limit=100&page="+i)); + } + + return result; + } + else { + return Collections.singletonList(readRemoteResource(remoteRestResource)); + } + } + /** * execute a request for remote content (e.g. GET) or a request * which does not expect contents back (e.g. DELETE) diff --git a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteAnnouncementsDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteAnnouncementsDAO.java index 2dbc5e072..427b93336 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteAnnouncementsDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteAnnouncementsDAO.java @@ -46,7 +46,7 @@ public RemoteAnnouncementsDAO(CacheAnnouncementsDAO cacheAnnouncementsDAO) { public List getAllAnnouncements() throws AnnouncementsRetrievalException { try { - JSONObject parentObject = readRemoteResouce("/announcements"); + JSONObject parentObject = readRemoteResource("/announcements"); if (cache != null) { try { diff --git a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteSensorDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteSensorDAO.java index adf645e2a..0bfbb05e3 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteSensorDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteSensorDAO.java @@ -56,18 +56,29 @@ public RemoteSensorDAO(CacheSensorDAO cacheSensorDAO) { public List getAllSensors() throws SensorRetrievalException { try { - JSONObject parentObject = readRemoteResouce("/sensors"); + List parentObject = readRemoteResource("/sensors", true); if (cache != null) { try { - cache.storeAllSensors(parentObject.toString()); + cache.storeAllSensors(parentObject); } catch (IOException e) { logger.warn(e.getMessage()); } } - return Car.fromJsonList(parentObject); + List result = null; + + for (JSONObject jsonObject : parentObject) { + if (result == null) { + result = Car.fromJsonList(jsonObject); + } + else { + result.addAll(Car.fromJsonList(jsonObject)); + } + } + + return SensorDAOUtil.sortByManufacturer(result); } catch (IOException e) { logger.warn(e.getMessage()); throw new SensorRetrievalException(e); diff --git a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTermsOfUseDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTermsOfUseDAO.java index de29b4afb..52f8e9738 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTermsOfUseDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTermsOfUseDAO.java @@ -45,7 +45,7 @@ public RemoteTermsOfUseDAO(CacheTermsOfUseDAO cacheTermsOfUseDAO) { @Override public TermsOfUse getTermsOfUse() throws TermsOfUseRetrievalException { try { - JSONObject parentObject = readRemoteResouce("/termsOfUse"); + JSONObject parentObject = readRemoteResource("/termsOfUse"); if (cache != null) { try { @@ -74,7 +74,7 @@ public TermsOfUse getTermsOfUse() throws TermsOfUseRetrievalException { @Override public TermsOfUseInstance getTermsOfUseInstance(String id) throws TermsOfUseRetrievalException { try { - JSONObject parentObject = readRemoteResouce("/termsOfUse/"+id); + JSONObject parentObject = readRemoteResource("/termsOfUse/"+id); if (cache != null) { try { diff --git a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTrackDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTrackDAO.java index 9c9a4d9ec..c2e0ecfe3 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTrackDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteTrackDAO.java @@ -87,7 +87,7 @@ public String storeTrack(Track track, boolean obfuscate) throws NotConnectedExce public Track getTrack(String id) throws NotConnectedException { Track result; try { - JSONObject parentObject = readRemoteResouce("/tracks/"+id); + JSONObject parentObject = readRemoteResource("/tracks/"+id); result = new TrackDecoder().fromJson(parentObject); } catch (ParseException e) { throw new NotConnectedException(e); diff --git a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteUserDAO.java b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteUserDAO.java index e9b27f955..62df9f3e5 100644 --- a/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteUserDAO.java +++ b/org.envirocar.app/src/org/envirocar/app/dao/remote/RemoteUserDAO.java @@ -59,7 +59,7 @@ public void updateUser(User user) throws UserUpdateException, UnauthorizedExcept @Override public User getUser(String id) throws UserRetrievalException, UnauthorizedException { try { - JSONObject json = readRemoteResouce("/users/"+id); + JSONObject json = readRemoteResource("/users/"+id); return User.fromJson(json); } catch (IOException e) { throw new UserRetrievalException(e); diff --git a/org.envirocar.app/src/org/envirocar/app/exception/ServerException.java b/org.envirocar.app/src/org/envirocar/app/exception/ServerException.java index 9c57705e9..bd869b67f 100644 --- a/org.envirocar.app/src/org/envirocar/app/exception/ServerException.java +++ b/org.envirocar.app/src/org/envirocar/app/exception/ServerException.java @@ -11,4 +11,8 @@ public ServerException(Exception e) { super(e); } + public ServerException(String string) { + super(string); + } + } diff --git a/org.envirocar.app/src/org/envirocar/app/json/TrackDecoder.java b/org.envirocar.app/src/org/envirocar/app/json/TrackDecoder.java index bac345791..89161c50a 100644 --- a/org.envirocar.app/src/org/envirocar/app/json/TrackDecoder.java +++ b/org.envirocar.app/src/org/envirocar/app/json/TrackDecoder.java @@ -32,6 +32,7 @@ import org.apache.http.ParseException; import org.apache.http.util.EntityUtils; import org.envirocar.app.dao.exception.TrackRetrievalException; +import org.envirocar.app.exception.ServerException; import org.envirocar.app.storage.DbAdapterImpl; import org.envirocar.app.storage.Track; import org.envirocar.app.util.Util; @@ -42,66 +43,11 @@ public class TrackDecoder { public Integer resolveTrackCount(HttpResponse response) throws TrackRetrievalException { - if (response.containsHeader("Link")) { - Header[] link = response.getHeaders("Link"); - - for (Header l : link) { - Integer result = resolveLastRel(l.getValue()); - if (result != null) { - return result; - } - } - - if (link.length > 0 && link[0].getValue() != null) { - throw new TrackRetrievalException("Could not parse the HTTP Header 'Link': "+link[0].getValue()); - } - else { - throw new TrackRetrievalException("Invalid HTTP Header 'Link'"); - } - } - else { - throw new TrackRetrievalException("Response did not contain the exepected HTTP Header 'Link'"); - } - - } - - public Integer resolveLastRel(String value) { - if (value != null) { - String[] split = value.split(","); - - for (String line : split) { - if (line.contains("rel=last")) { - String[] params = line.split(";"); - if (params != null && params.length > 0) { - return resolvePageValue(params[0]); - } - } - } - } - return null; - } - - public Integer resolvePageValue(String sourceUrl) { - String url; - if (sourceUrl.startsWith("<")) { - url = sourceUrl.substring(1, sourceUrl.length()-1); - } - else { - url = sourceUrl; - } - - if (url.contains("?")) { - int index = url.indexOf("?")+1; - if (index != url.length()) { - String params = url.substring(index, url.length()); - for (String kvp : params.split("&")) { - if (kvp.startsWith("page")) { - return Integer.parseInt(kvp.substring(kvp.indexOf("page")+5)); - } - } - } + try { + return Util.resolveResourceCount(response); + } catch (ServerException e) { + throw new TrackRetrievalException(e); } - return null; } public List getResourceIds(InputStream response) throws ParseException, IOException, JSONException { diff --git a/org.envirocar.app/src/org/envirocar/app/model/Car.java b/org.envirocar.app/src/org/envirocar/app/model/Car.java index c1afe85c0..da67ab464 100644 --- a/org.envirocar.app/src/org/envirocar/app/model/Car.java +++ b/org.envirocar.app/src/org/envirocar/app/model/Car.java @@ -124,11 +124,13 @@ public String toString() { sb.append(manufacturer); sb.append(" "); sb.append(model); + sb.append(" "); + sb.append(constructionYear); sb.append(" ("); sb.append(fuelType); sb.append(" / "); - sb.append(constructionYear); - sb.append(")"); + sb.append(engineDisplacement); + sb.append("cc)"); return sb.toString(); } @@ -197,7 +199,7 @@ public static List fromJsonList(JSONObject json) throws JSONException { try { sensors.add(Car.fromJsonWithStrictEngineDisplacement(properties)); } catch (JSONException e) { - logger.warn(String.format("Car '%s' not supported: %s", carId != null ? carId : "null", e.getMessage())); + logger.verbose(String.format("Car '%s' not supported: %s", carId != null ? carId : "null", e.getMessage())); } } } @@ -205,6 +207,7 @@ public static List fromJsonList(JSONObject json) throws JSONException { return sensors; } + public static FuelType resolveFuelType(String foolType) { if (foolType.equals(GASOLINE_STRING)) { return FuelType.GASOLINE; diff --git a/org.envirocar.app/src/org/envirocar/app/util/Util.java b/org.envirocar.app/src/org/envirocar/app/util/Util.java index 7173b40b7..abcdb3e2e 100644 --- a/org.envirocar.app/src/org/envirocar/app/util/Util.java +++ b/org.envirocar.app/src/org/envirocar/app/util/Util.java @@ -46,6 +46,9 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.envirocar.app.exception.ServerException; import org.envirocar.app.json.TrackEncoder; import org.envirocar.app.logging.Logger; import org.envirocar.app.storage.Measurement; @@ -401,5 +404,67 @@ public static File saveTrackAndReturnFile(Track t, boolean obfuscate) throws JSO return Util.saveTrackToSdCard(new TrackEncoder().createTrackJson(t, obfuscate).toString(), (t.isRemoteTrack() ? t.getRemoteID() : Long.toString(t.getId()))); } + + public static Integer resolveResourceCount(HttpResponse response) throws ServerException { + if (response.containsHeader("Link")) { + Header[] link = response.getHeaders("Link"); + + for (Header l : link) { + Integer result = resolveLastRel(l.getValue()); + if (result != null) { + return result; + } + } + + if (link.length > 0 && link[0].getValue() != null) { + throw new ServerException("Could not parse the HTTP Header 'Link': "+link[0].getValue()); + } + else { + throw new ServerException("Invalid HTTP Header 'Link'"); + } + } + else { + throw new ServerException("Response did not contain the exepected HTTP Header 'Link'"); + } + } + + private static Integer resolveLastRel(String value) { + if (value != null) { + String[] split = value.split(","); + + for (String line : split) { + if (line.contains("rel=last")) { + String[] params = line.split(";"); + if (params != null && params.length > 0) { + return resolvePageValue(params[0]); + } + } + } + } + return null; + } + + private static Integer resolvePageValue(String sourceUrl) { + String url; + if (sourceUrl.startsWith("<")) { + url = sourceUrl.substring(1, sourceUrl.length()-1); + } + else { + url = sourceUrl; + } + + if (url.contains("?")) { + int index = url.indexOf("?")+1; + if (index != url.length()) { + String params = url.substring(index, url.length()); + for (String kvp : params.split("&")) { + if (kvp.startsWith("page")) { + return Integer.parseInt(kvp.substring(kvp.indexOf("page")+5)); + } + } + } + } + return null; + } }