/* Montréal Just in Case Copyright (C) 2011 Mudar Noufal <mn@mudar.ca> Geographic locations of public safety services. A Montréal Open Data project. This file is part of Montréal Just in Case. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ca.mudar.mtlaucasou.service; import android.app.IntentService; import android.content.Context; import android.content.Intent; import android.support.annotation.RawRes; import android.util.Log; import com.google.gson.Gson; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Date; import ca.mudar.mtlaucasou.Const; import ca.mudar.mtlaucasou.Const.LayerTypes; import ca.mudar.mtlaucasou.Const.MapTypes; import ca.mudar.mtlaucasou.R; import ca.mudar.mtlaucasou.api.ApiClient; import ca.mudar.mtlaucasou.data.RealmQueries; import ca.mudar.mtlaucasou.data.UserPrefs; import ca.mudar.mtlaucasou.model.LayerType; import ca.mudar.mtlaucasou.model.MapType; import ca.mudar.mtlaucasou.model.geojson.PointsFeatureCollection; import ca.mudar.mtlaucasou.model.jsonapi.Attributes; import ca.mudar.mtlaucasou.model.jsonapi.DataItem; import ca.mudar.mtlaucasou.model.jsonapi.HelloApi; import ca.mudar.mtlaucasou.util.ApiDataUtils; import ca.mudar.mtlaucasou.util.LogUtils; import io.realm.Realm; import retrofit2.Response; import static ca.mudar.mtlaucasou.util.LogUtils.makeLogTag; public class SyncService extends IntentService { private static final String TAG = makeLogTag("SyncService"); private Realm mRealm; public static Intent newIntent(Context context) { return new Intent(context, SyncService.class); } public SyncService() { super(TAG); } @Override protected void onHandleIntent(Intent intent) { mRealm = Realm.getDefaultInstance(); final long startTime = System.currentTimeMillis(); final UserPrefs userPrefs = UserPrefs.getInstance(this); if (!userPrefs.hasLoadedData()) { loadInitialLocalData(); } else { // TODO handle updates frequency and redundancy downloadRemoteUpdatesIfAvailable(userPrefs); } Log.v(TAG, String.format("Data sync duration: %dms", System.currentTimeMillis() - startTime)); mRealm.close(); } private void loadInitialLocalData() { mRealm.beginTransaction(); importLocalData(R.raw.fire_halls, MapTypes.FIRE_HALLS, LayerTypes.FIRE_HALLS); importLocalData(R.raw.spvm_stations, MapTypes.SPVM_STATIONS, LayerTypes.SPVM_STATIONS); importLocalData(R.raw.water_supplies, MapTypes.HEAT_WAVE, LayerTypes._HEAT_WAVE_MIXED); importLocalData(R.raw.air_conditioning, MapTypes.HEAT_WAVE, LayerTypes.AIR_CONDITIONING); importLocalData(R.raw.emergency_hostels, MapTypes.EMERGENCY_HOSTELS, LayerTypes.EMERGENCY_HOSTELS); importLocalData(R.raw.hospitals, MapTypes.HEALTH, LayerTypes.HOSPITALS); importLocalData(R.raw.clsc, MapTypes.HEALTH, LayerTypes.CLSC); mRealm.commitTransaction(); } private void downloadRemoteUpdatesIfAvailable(UserPrefs prefs) { try { Response<HelloApi> helloResponse = ApiClient.hello(ApiClient.getService()); if (helloResponse != null && helloResponse.body() != null) { final HelloApi api = helloResponse.body(); for (DataItem dataset : api.getData()) { if (!Const.ApiValues.TYPE_PLACEMARKS.equals(dataset.getType())) { continue; } final String key = ApiDataUtils.getSharedPrefsKey(dataset.getId()); final Date updatedAt = dataset.getAttributes().getUpdated(); if (prefs.isApiDataNewer(key, updatedAt)) { final boolean result = importRemoteData(dataset); if (result) { prefs.setDataUpdatedAt(key, updatedAt); } } } } } catch (Exception e) { LogUtils.REMOTE_LOG(e); } } private void importLocalData(@RawRes int resource, @MapType String mapType, @LayerType String layerType) { final InputStream inputStream = getResources().openRawResource(resource); final InputStreamReader inputStreamReader = new InputStreamReader(inputStream); final PointsFeatureCollection collection = new Gson() .fromJson(inputStreamReader, PointsFeatureCollection.class); RealmQueries.cacheMapData(mRealm, collection.getFeatures(), mapType, layerType, false); } /** * Request the GeoJSON data from the API * * @param dataset The dataset to import into the Realm db * @return */ private boolean importRemoteData(DataItem dataset) { final Response<PointsFeatureCollection> response = ApiClient .getPlacemarks(ApiClient.getService(), dataset.getLinks().getSelf()); if (response != null) { PointsFeatureCollection collection = response.body(); if (collection != null && collection.getFeatures() != null) { final Attributes attributes = dataset.getAttributes(); if (attributes != null) { RealmQueries.clearMapData(mRealm, attributes.getLayerType()); RealmQueries.cacheMapData(mRealm, collection.getFeatures(), attributes.getMapType(), attributes.getLayerType(), true); } } return true; } return false; } }