/*
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;
}
}