package net.osmand.plus.activities;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import net.osmand.LogUtil;
import net.osmand.SunriseSunset;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.OsmandSettings.DayNightMode;
import org.apache.commons.logging.Log;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationManager;
/**
* Class to help determine if we want to render day or night map - it uses the
* DayNightMode enumeration for its behavior<BR>
* - it uses the LightSensor and needs calls from MapActivity on onPause and onResume to
* register/unregister the sensor listener<BR>
* - it uses the {@link SunriseSunset} and {@link LocationManager} to find
* out about sunset/sunrise and use it
*
* Note: the usage of SunriseSunset is not optimized in any way, it is
* recalculated on each demand. If this way it would be resource consuming, some
* recalculation threshold could be specified to recalculate the sun-rise/set
* only sometimes.<BR>
* Note2: the light sensor threshold is hard coded to
* {@link SensorManager#LIGHT_CLOUDY} and could be made customizable
*
* @author pavol.zibrita
*/
public class DayNightHelper implements SensorEventListener {
private static final Log log = LogUtil.getLog(DayNightHelper.class);
private final OsmandApplication osmandApplication;
public DayNightHelper(OsmandApplication osmandApplication) {
this.osmandApplication = osmandApplication;
setDayNightMode(OsmandSettings.getDayNightMode(OsmandSettings
.getPrefs(osmandApplication)));
}
DayNightMode dayNightMode = DayNightMode.AUTO;
private DayNightHelper listener;
private float lux = SensorManager.LIGHT_SUNLIGHT;
private long lastAutoCall = 0;
private Boolean lastAutoValue = null;
public void setDayNightMode(DayNightMode mode) {
if (this.dayNightMode != mode) {
this.dayNightMode = mode;
osmandApplication.getResourceManager().getRenderer().clearCache();
unregisterServiceListener();
if(dayNightMode.isSensor()){
registerServiceListener();
}
}
}
/**
* @return null if could not be determined (in case of error)
* @return true if day is supposed to be
*/
public Boolean getDayNightRenderer() {
if (dayNightMode.isDay()) {
return Boolean.TRUE;
} else if (dayNightMode.isNight()) {
return Boolean.FALSE;
} else // We are in auto mode!
if (dayNightMode.isAuto()) {
long currentTime = System.currentTimeMillis();
//allow recalculation each 5 seconds
if (currentTime - lastAutoCall > 5000) {
lastAutoCall = System.currentTimeMillis();
try {
Location lastKnownLocation = getLocation();
if(lastKnownLocation == null){
return null;
}
double longitude = lastKnownLocation.getLongitude();
Date actualTime = new Date();
SunriseSunset daynightSwitch = new SunriseSunset(lastKnownLocation.getLatitude(), longitude < 0 ? 360 - longitude : longitude ,
actualTime, TimeZone.getDefault().getOffset(actualTime.getTime())/3600000);
boolean daytime = daynightSwitch.isDaytime();
log.debug("Sunrise/sunset setting to day: " + daytime); //$NON-NLS-1$
lastAutoValue = Boolean.valueOf(daytime);
return lastAutoValue;
} catch (IllegalArgumentException e) {
log.warn("Network location provider not available"); //$NON-NLS-1$
} catch (SecurityException e) {
log.warn("Missing permitions to get actual location!"); //$NON-NLS-1$
}
return null;
} else {
return lastAutoValue;
}
} else if (dayNightMode.isSensor()) {
log.debug("lux value:" + lux + " setting to day: " + (lux > SensorManager.LIGHT_CLOUDY)); //$NON-NLS-1$ //$NON-NLS-2$
return lux > SensorManager.LIGHT_CLOUDY ? Boolean.TRUE : Boolean.FALSE;
}
return null;
}
private Location getLocation() {
Location lastKnownLocation = null;
LocationManager locationProvider = (LocationManager) osmandApplication.getSystemService(Context.LOCATION_SERVICE);
List<String> providers = new ArrayList<String>(locationProvider.getProviders(true));
//note, passive provider is from API_LEVEL 8 but it is a constant, we can check for it.
// constant should not be changed in future
int passiveFirst = providers.indexOf("passive"); //LocationManager.PASSIVE_PROVIDER
//put passive provider to first place
if (passiveFirst > -1) {
providers.add(0,providers.remove(passiveFirst));
}
//find location
for (String provider : providers) {
lastKnownLocation = locationProvider.getLastKnownLocation(provider);
if (lastKnownLocation == null) {
break;
}
}
return lastKnownLocation;
}
public void onMapPause() {
unregisterServiceListener();
}
private void unregisterServiceListener() {
if (listener != null) {
SensorManager mSensorManager = (SensorManager) osmandApplication
.getSystemService(Context.SENSOR_SERVICE);
Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
mSensorManager.unregisterListener(listener, mLight);
listener = null;
}
}
public void onMapResume() {
registerServiceListener();
}
private void registerServiceListener() {
if (listener == null && dayNightMode.isSensor()) {
SensorManager mSensorManager = (SensorManager) osmandApplication.getSystemService(Context.SENSOR_SERVICE);
Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
List<Sensor> list = mSensorManager.getSensorList(Sensor.TYPE_LIGHT);
log.info("Light sensors:" + list.size()); //$NON-NLS-1$
mSensorManager.registerListener(this, mLight,
SensorManager.SENSOR_DELAY_NORMAL);
listener = this;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// nothing to do here
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.values.length > 0) {
lux = event.values[0];
}
}
}