package de.westnordost.streetcomplete.data;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import com.mapzen.android.lost.api.LocationListener;
import com.mapzen.android.lost.api.LocationRequest;
import com.mapzen.android.lost.api.LocationServices;
import com.mapzen.android.lost.api.LostApiClient;
import javax.inject.Inject;
import de.westnordost.osmapi.map.data.LatLon;
import de.westnordost.osmapi.map.data.OsmLatLon;
import de.westnordost.streetcomplete.Prefs;
import de.westnordost.streetcomplete.data.changesets.OpenChangesetsDao;
import de.westnordost.streetcomplete.data.download.MobileDataAutoDownloadStrategy;
import de.westnordost.streetcomplete.data.download.QuestAutoDownloadStrategy;
import de.westnordost.streetcomplete.data.download.WifiAutoDownloadStrategy;
import de.westnordost.streetcomplete.data.osm.upload.ChangesetAutoCloserReceiver;
import de.westnordost.streetcomplete.util.SphericalEarthMath;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
/** Automatically downloads and uploads new quests around the user's location and uploads quests.
*
* Respects the user preference to only sync on wifi or not sync automatically at all
* */
public class QuestAutoSyncer implements LocationListener, LostApiClient.ConnectionCallbacks
{
private static final String TAG_AUTO_DOWNLOAD = "AutoQuestSyncer";
private final QuestController questController;
private final MobileDataAutoDownloadStrategy mobileDataDownloadStrategy;
private final WifiAutoDownloadStrategy wifiDownloadStrategy;
private final Context context;
private final SharedPreferences prefs;
private LostApiClient lostApiClient;
private LatLon pos;
private boolean isConnected;
private boolean isWifi;
@Inject public QuestAutoSyncer(QuestController questController,
MobileDataAutoDownloadStrategy mobileDataDownloadStrategy,
WifiAutoDownloadStrategy wifiDownloadStrategy,
Context context, SharedPreferences prefs)
{
this.questController = questController;
this.mobileDataDownloadStrategy = mobileDataDownloadStrategy;
this.wifiDownloadStrategy = wifiDownloadStrategy;
this.context = context;
this.prefs = prefs;
lostApiClient = new LostApiClient.Builder(context).addConnectionCallbacks(this).build();
}
public void onStart()
{
updateConnectionState();
context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
public void onStop()
{
stopPositionTracking();
context.unregisterReceiver(connectivityReceiver);
}
public void startPositionTracking()
{
if(!lostApiClient.isConnected()) lostApiClient.connect();
}
public void stopPositionTracking()
{
try // TODO remove when https://github.com/mapzen/lost/issues/178 is solved
{
if(lostApiClient.isConnected())
{
LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this);
lostApiClient.disconnect();
}
} catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void onConnected() throws SecurityException
{
LocationRequest request = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setSmallestDisplacement(500)
.setInterval(3 * 60 * 1000); // 3 minutes
LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this);
}
@Override
public void onConnectionSuspended() {}
@Override public void onLocationChanged(Location location)
{
LatLon pos = new OsmLatLon(location.getLatitude(), location.getLongitude());
// TODO remove when https://github.com/mapzen/lost/issues/142 is fixed
if(this.pos != null)
{
if(SphericalEarthMath.distance(pos, this.pos) < 400) return;
}
this.pos = pos;
triggerAutoDownload();
}
@Override public void onProviderEnabled(String provider) {}
@Override public void onProviderDisabled(String provider) {}
public void triggerAutoDownload()
{
if(!isAllowedByPreference()) return;
if(pos == null) return;
if(!isConnected) return;
if(questController.isPriorityDownloadRunning()) return;
Log.i(TAG_AUTO_DOWNLOAD, "Checking whether to automatically download new quests at "
+ pos.getLatitude() + "," + pos.getLongitude());
final QuestAutoDownloadStrategy downloadStrategy = isWifi ? wifiDownloadStrategy : mobileDataDownloadStrategy;
new Thread(){ @Override public void run() {
if(!downloadStrategy.mayDownloadHere(pos)) return;
questController.download(
downloadStrategy.getDownloadBoundingBox(pos),
downloadStrategy.getQuestTypeDownloadCount(), false);
}}.start();
}
public void triggerAutoUpload()
{
if(!isAllowedByPreference()) return;
if(!isConnected) return;
questController.upload();
triggerDelayedClosingOfChangesets();
}
private void triggerDelayedClosingOfChangesets()
{
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
long delayTime = System.currentTimeMillis() + OpenChangesetsDao.CLOSE_CHANGESETS_AFTER_INACTIVITY_OF;
Intent intent = new Intent(context, ChangesetAutoCloserReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, FLAG_CANCEL_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, delayTime, pi);
}
private boolean updateConnectionState()
{
ConnectivityManager connectivityManager
= (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
boolean newIsConnected = info != null && info.isConnected();
boolean newIsWifi = newIsConnected && info.getType() == ConnectivityManager.TYPE_WIFI;
boolean result = newIsConnected != isConnected || newIsWifi != isWifi;
isConnected = newIsConnected;
isWifi = newIsWifi;
return result;
}
private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver()
{
@Override public void onReceive(Context context, Intent intent)
{
boolean connectionStateChanged = updateConnectionState();
// connecting to i.e. mobile data after being disconnected from wifi -> not interested in that
boolean isFailover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
if(!isFailover && connectionStateChanged && isConnected)
{
triggerAutoDownload();
triggerAutoUpload();
}
}
};
private boolean isAllowedByPreference()
{
Prefs.Autosync p = Prefs.Autosync.valueOf(prefs.getString(Prefs.AUTOSYNC,"ON"));
return p == Prefs.Autosync.ON || p == Prefs.Autosync.WIFI && isWifi;
}
}