Revert "lineage-sdk: Switch back to AOSP TwilightService"
* Causing issues on several devices, with that the GPS gets stuck in
ON state and can't be turned off
* No timezone fallback
This reverts commit cac65b8972.
Change-Id: Ie9b0249a1e6f94c374e90f89f49d506b1ec6c50d
This commit is contained in:
committed by
Sam Mortimer
parent
e703ff0a0b
commit
f13838a099
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The CyanogenMod Project
|
||||
* Copyright (C) 2018 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +26,7 @@ import android.util.Range;
|
||||
import android.util.Slog;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import com.android.server.twilight.TwilightState;
|
||||
import org.lineageos.platform.internal.display.TwilightTracker.TwilightState;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.BitSet;
|
||||
@@ -192,15 +191,7 @@ public class ColorTemperatureController extends LiveDisplayFeature {
|
||||
} else if (mode == MODE_NIGHT) {
|
||||
temperature = mNightTemperature;
|
||||
} else if (mode == MODE_AUTO) {
|
||||
final int twilightTemp = getTwilightK();
|
||||
if (twilightTemp >= 0) {
|
||||
temperature = twilightTemp;
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "updateColorTemperature: getTwilightK returned < 0; "
|
||||
+ "maintaining existing temperature");
|
||||
}
|
||||
}
|
||||
temperature = getTwilightK();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
@@ -212,7 +203,6 @@ public class ColorTemperatureController extends LiveDisplayFeature {
|
||||
|
||||
if (isTransitioning()) {
|
||||
// fire again in a minute
|
||||
mHandler.removeCallbacks(mTransitionRunnable);
|
||||
mHandler.postDelayed(mTransitionRunnable, DateUtils.MINUTE_IN_MILLIS);
|
||||
}
|
||||
}
|
||||
@@ -270,10 +260,6 @@ public class ColorTemperatureController extends LiveDisplayFeature {
|
||||
}
|
||||
|
||||
private synchronized void setDisplayTemperature(int temperature) {
|
||||
if (temperature < 0) {
|
||||
// We're in not yet initialized state, silently ignore.
|
||||
return;
|
||||
}
|
||||
if (!mColorTemperatureRange.contains(temperature)) {
|
||||
Slog.e(TAG, "Color temperature out of range: " + temperature);
|
||||
return;
|
||||
@@ -296,47 +282,50 @@ public class ColorTemperatureController extends LiveDisplayFeature {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Where is the sun anyway? This calculation determines day or night, and scales
|
||||
* the value around sunset/sunrise for a smooth transition.
|
||||
*
|
||||
* @param now
|
||||
* @param sunset
|
||||
* @param sunrise
|
||||
* @return float between 0 and 1
|
||||
*/
|
||||
private static float adj(long now, long sunset, long sunrise) {
|
||||
if (sunset < 0 || sunrise < 0
|
||||
|| now < sunset || now > (sunrise + TWILIGHT_ADJUSTMENT_TIME)) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (now <= (sunset + TWILIGHT_ADJUSTMENT_TIME)) {
|
||||
return MathUtils.lerp(1.0f, 0.0f,
|
||||
(float) (now - sunset) / TWILIGHT_ADJUSTMENT_TIME);
|
||||
}
|
||||
|
||||
if (now >= sunrise) {
|
||||
return MathUtils.lerp(1.0f, 0.0f,
|
||||
(float) ((sunrise + TWILIGHT_ADJUSTMENT_TIME) - now) / TWILIGHT_ADJUSTMENT_TIME);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the color temperature we should use for the display based on
|
||||
* the position of the sun.
|
||||
*
|
||||
* @return color temperature in Kelvin or -1 if current state can't be determined.
|
||||
* @return color temperature in Kelvin
|
||||
*/
|
||||
private int getTwilightK() {
|
||||
float adjustment = 1.0f;
|
||||
final TwilightState twilight = getTwilight();
|
||||
if (twilight == null) {
|
||||
return -1;
|
||||
|
||||
if (twilight != null) {
|
||||
final long now = System.currentTimeMillis();
|
||||
adjustment = adj(now, twilight.getYesterdaySunset(), twilight.getTodaySunrise()) *
|
||||
adj(now, twilight.getTodaySunset(), twilight.getTomorrowSunrise());
|
||||
}
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
final long sunrise = twilight.sunriseTimeMillis();
|
||||
final long sunset = twilight.sunsetTimeMillis();
|
||||
final float adjustment;
|
||||
|
||||
// Sanity checks
|
||||
if (sunrise <= 0 || sunset <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (now >= sunrise && now < sunset) {
|
||||
// It's daytime
|
||||
if (now < sunrise + TWILIGHT_ADJUSTMENT_TIME) {
|
||||
adjustment = MathUtils.lerp(0.0f, 1.0f, (float) (now - sunrise) /
|
||||
TWILIGHT_ADJUSTMENT_TIME);
|
||||
} else {
|
||||
adjustment = 1.0f;
|
||||
}
|
||||
} else if (now >= sunset && now < sunrise) {
|
||||
// It's nighttime
|
||||
if (now < sunset + TWILIGHT_ADJUSTMENT_TIME) {
|
||||
adjustment = MathUtils.lerp(1.0f, 0.0f, (float) (now - sunset) /
|
||||
TWILIGHT_ADJUSTMENT_TIME);
|
||||
} else {
|
||||
adjustment = 0.0f;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return (int)MathUtils.lerp(mNightTemperature, mDayTemperature, adjustment);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,9 @@ import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.server.twilight.TwilightState;
|
||||
|
||||
import org.lineageos.platform.internal.common.UserContentObserver;
|
||||
import org.lineageos.platform.internal.display.LiveDisplayService.State;
|
||||
import org.lineageos.platform.internal.display.TwilightTracker.TwilightState;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.BitSet;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The CyanogenMod Project
|
||||
* Copyright (C) 2018 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,12 +35,11 @@ import android.view.Display;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.power.BatterySaverPolicy.ServiceType;
|
||||
import com.android.server.twilight.TwilightListener;
|
||||
import com.android.server.twilight.TwilightManager;
|
||||
import com.android.server.twilight.TwilightState;
|
||||
|
||||
import org.lineageos.platform.internal.LineageSystemService;
|
||||
import org.lineageos.platform.internal.common.UserContentObserver;
|
||||
import org.lineageos.platform.internal.display.TwilightTracker.TwilightListener;
|
||||
import org.lineageos.platform.internal.display.TwilightTracker.TwilightState;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -83,7 +81,7 @@ public class LiveDisplayService extends LineageSystemService {
|
||||
|
||||
private DisplayManager mDisplayManager;
|
||||
private ModeObserver mModeObserver;
|
||||
private TwilightManager mTwilightManager;
|
||||
private final TwilightTracker mTwilightTracker;
|
||||
|
||||
private boolean mAwaitingNudge = true;
|
||||
private boolean mSunset = false;
|
||||
@@ -132,6 +130,8 @@ public class LiveDisplayService extends LineageSystemService {
|
||||
Process.THREAD_PRIORITY_DEFAULT, false /*allowIo*/);
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
|
||||
mTwilightTracker = new TwilightTracker(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,9 +202,8 @@ public class LiveDisplayService extends LineageSystemService {
|
||||
mState.mLowPowerMode =
|
||||
pmi.getLowPowerState(SERVICE_TYPE_DUMMY).globalBatterySaverEnabled;
|
||||
|
||||
mTwilightManager = getLocalService(TwilightManager.class);
|
||||
mTwilightManager.registerListener(mTwilightListener, mHandler);
|
||||
mState.mTwilight = mTwilightManager.getLastTwilightState();
|
||||
mTwilightTracker.registerListener(mTwilightListener, mHandler);
|
||||
mState.mTwilight = mTwilightTracker.getCurrentState();
|
||||
|
||||
if (mConfig.hasModeSupport()) {
|
||||
mModeObserver = new ModeObserver(mHandler);
|
||||
@@ -374,7 +373,7 @@ public class LiveDisplayService extends LineageSystemService {
|
||||
|
||||
@Override
|
||||
public boolean isNight() {
|
||||
final TwilightState twilight = mTwilightManager.getLastTwilightState();
|
||||
final TwilightState twilight = mTwilightTracker.getCurrentState();
|
||||
return twilight != null && twilight.isNight();
|
||||
}
|
||||
};
|
||||
@@ -466,8 +465,8 @@ public class LiveDisplayService extends LineageSystemService {
|
||||
// Night watchman
|
||||
private final TwilightListener mTwilightListener = new TwilightListener() {
|
||||
@Override
|
||||
public void onTwilightStateChanged(TwilightState state) {
|
||||
mState.mTwilight = state;
|
||||
public void onTwilightStateChanged() {
|
||||
mState.mTwilight = mTwilightTracker.getCurrentState();
|
||||
updateFeatures(TWILIGHT_CHANGED);
|
||||
nudge();
|
||||
}
|
||||
@@ -512,7 +511,7 @@ public class LiveDisplayService extends LineageSystemService {
|
||||
* @param state
|
||||
*/
|
||||
private void nudge() {
|
||||
final TwilightState twilight = mTwilightManager.getLastTwilightState();
|
||||
final TwilightState twilight = mTwilightTracker.getCurrentState();
|
||||
if (!mAwaitingNudge || twilight == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.platform.internal.display;
|
||||
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.FloatMath;
|
||||
|
||||
/** @hide */
|
||||
public class TwilightCalculator {
|
||||
|
||||
/** Value of {@link #mState} if it is currently day */
|
||||
public static final int DAY = 0;
|
||||
|
||||
/** Value of {@link #mState} if it is currently night */
|
||||
public static final int NIGHT = 1;
|
||||
|
||||
private static final float DEGREES_TO_RADIANS = (float) (Math.PI / 180.0f);
|
||||
|
||||
// element for calculating solar transit.
|
||||
private static final float J0 = 0.0009f;
|
||||
|
||||
// correction for civil twilight
|
||||
private static final float ALTIDUTE_CORRECTION_CIVIL_TWILIGHT = -0.104719755f;
|
||||
|
||||
// coefficients for calculating Equation of Center.
|
||||
private static final float C1 = 0.0334196f;
|
||||
private static final float C2 = 0.000349066f;
|
||||
private static final float C3 = 0.000005236f;
|
||||
|
||||
private static final float OBLIQUITY = 0.40927971f;
|
||||
|
||||
// Java time on Jan 1, 2000 12:00 UTC.
|
||||
private static final long UTC_2000 = 946728000000L;
|
||||
|
||||
/**
|
||||
* Time of sunset (civil twilight) in milliseconds or -1 in the case the day
|
||||
* or night never ends.
|
||||
*/
|
||||
public long mSunset;
|
||||
|
||||
/**
|
||||
* Time of sunrise (civil twilight) in milliseconds or -1 in the case the
|
||||
* day or night never ends.
|
||||
*/
|
||||
public long mSunrise;
|
||||
|
||||
/** Current state */
|
||||
public int mState;
|
||||
|
||||
/**
|
||||
* calculates the civil twilight bases on time and geo-coordinates.
|
||||
*
|
||||
* @param time time in milliseconds.
|
||||
* @param latiude latitude in degrees.
|
||||
* @param longitude latitude in degrees.
|
||||
*/
|
||||
public void calculateTwilight(long time, double latiude, double longitude) {
|
||||
final float daysSince2000 = (float) (time - UTC_2000) / DateUtils.DAY_IN_MILLIS;
|
||||
|
||||
// mean anomaly
|
||||
final float meanAnomaly = 6.240059968f + daysSince2000 * 0.01720197f;
|
||||
|
||||
// true anomaly
|
||||
final float trueAnomaly = meanAnomaly + C1 * FloatMath.sin(meanAnomaly) + C2
|
||||
* FloatMath.sin(2 * meanAnomaly) + C3 * FloatMath.sin(3 * meanAnomaly);
|
||||
|
||||
// ecliptic longitude
|
||||
final float solarLng = trueAnomaly + 1.796593063f + (float) Math.PI;
|
||||
|
||||
// solar transit in days since 2000
|
||||
final double arcLongitude = -longitude / 360;
|
||||
float n = Math.round(daysSince2000 - J0 - arcLongitude);
|
||||
double solarTransitJ2000 = n + J0 + arcLongitude + 0.0053f * FloatMath.sin(meanAnomaly)
|
||||
+ -0.0069f * FloatMath.sin(2 * solarLng);
|
||||
|
||||
// declination of sun
|
||||
double solarDec = Math.asin(FloatMath.sin(solarLng) * FloatMath.sin(OBLIQUITY));
|
||||
|
||||
final double latRad = latiude * DEGREES_TO_RADIANS;
|
||||
|
||||
double cosHourAngle = (FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
|
||||
* Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec));
|
||||
// The day or night never ends for the given date and location, if this value is out of
|
||||
// range.
|
||||
if (cosHourAngle >= 1) {
|
||||
mState = NIGHT;
|
||||
mSunset = -1;
|
||||
mSunrise = -1;
|
||||
return;
|
||||
} else if (cosHourAngle <= -1) {
|
||||
mState = DAY;
|
||||
mSunset = -1;
|
||||
mSunrise = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI));
|
||||
|
||||
mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
|
||||
mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
|
||||
|
||||
if (mSunrise < time && mSunset > time) {
|
||||
mState = DAY;
|
||||
} else {
|
||||
mState = NIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,562 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.platform.internal.display;
|
||||
|
||||
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.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
import libcore.util.Objects;
|
||||
|
||||
/**
|
||||
* Figures out whether it's twilight time based on the user's location.
|
||||
*
|
||||
* Used by the UI mode manager and other components to adjust night mode
|
||||
* effects based on sunrise and sunset.
|
||||
*/
|
||||
public final class TwilightTracker {
|
||||
private static final String TAG = "TwilightTracker";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String ACTION_UPDATE_TWILIGHT_STATE =
|
||||
"lineageos.platform.intent.action.UPDATE_TWILIGHT_STATE";
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private final AlarmManager mAlarmManager;
|
||||
private final LocationManager mLocationManager;
|
||||
private final LocationHandler mLocationHandler;
|
||||
|
||||
private final ArrayList<TwilightListenerRecord> mListeners =
|
||||
new ArrayList<TwilightListenerRecord>();
|
||||
|
||||
private TwilightState mTwilightState;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public TwilightTracker(Context context) {
|
||||
mContext = context;
|
||||
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
||||
mLocationManager = (LocationManager) mContext.getSystemService(
|
||||
Context.LOCATION_SERVICE);
|
||||
mLocationHandler = new LocationHandler();
|
||||
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_TIME_CHANGED);
|
||||
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
|
||||
filter.addAction(ACTION_UPDATE_TWILIGHT_STATE);
|
||||
mContext.registerReceiver(mUpdateLocationReceiver, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current twilight state.
|
||||
*
|
||||
* @return The current twilight state, or null if no information is available.
|
||||
*/
|
||||
public TwilightState getCurrentState() {
|
||||
synchronized (mLock) {
|
||||
return mTwilightState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for twilight time.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void registerListener(TwilightListener listener, Handler handler) {
|
||||
synchronized (mLock) {
|
||||
mListeners.add(new TwilightListenerRecord(listener, handler));
|
||||
|
||||
if (mListeners.size() == 1) {
|
||||
mLocationHandler.enableLocationUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setTwilightState(TwilightState state) {
|
||||
synchronized (mLock) {
|
||||
if (!Objects.equal(mTwilightState, state)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Twilight state changed: " + state);
|
||||
}
|
||||
|
||||
mTwilightState = state;
|
||||
|
||||
final int listenerLen = mListeners.size();
|
||||
for (int i = 0; i < listenerLen; i++) {
|
||||
mListeners.get(i).postUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The user has moved if the accuracy circles of the two locations don't overlap.
|
||||
private static boolean hasMoved(Location from, Location to) {
|
||||
if (to == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (from == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if new location is older than the current one, the device hasn't moved.
|
||||
if (to.getElapsedRealtimeNanos() < from.getElapsedRealtimeNanos()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the distance between the two points.
|
||||
float distance = from.distanceTo(to);
|
||||
|
||||
// Get the total accuracy radius for both locations.
|
||||
float totalAccuracy = from.getAccuracy() + to.getAccuracy();
|
||||
|
||||
// If the distance is greater than the combined accuracy of the two
|
||||
// points then they can't overlap and hence the user has moved.
|
||||
return distance >= totalAccuracy;
|
||||
}
|
||||
|
||||
private final class LocationHandler extends Handler {
|
||||
private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
|
||||
private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
|
||||
private static final int MSG_PROCESS_NEW_LOCATION = 3;
|
||||
private static final int MSG_DO_TWILIGHT_UPDATE = 4;
|
||||
|
||||
private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
|
||||
private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
|
||||
private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
|
||||
private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
|
||||
private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX =
|
||||
15 * DateUtils.MINUTE_IN_MILLIS;
|
||||
private static final double FACTOR_GMT_OFFSET_LONGITUDE =
|
||||
1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
|
||||
|
||||
private boolean mPassiveListenerEnabled;
|
||||
private boolean mNetworkListenerEnabled;
|
||||
private boolean mDidFirstInit;
|
||||
private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
|
||||
private long mLastUpdateInterval;
|
||||
private Location mLocation;
|
||||
private final TwilightCalculator mTwilightCalculator = new TwilightCalculator();
|
||||
|
||||
public void processNewLocation(Location location) {
|
||||
Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location);
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
public void enableLocationUpdates() {
|
||||
sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
|
||||
}
|
||||
|
||||
public void requestLocationUpdate() {
|
||||
sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
|
||||
}
|
||||
|
||||
public void requestTwilightUpdate() {
|
||||
sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_PROCESS_NEW_LOCATION: {
|
||||
final Location location = (Location) msg.obj;
|
||||
final boolean hasMoved = hasMoved(mLocation, location);
|
||||
final boolean hasBetterAccuracy = mLocation == null
|
||||
|| location.getAccuracy() < mLocation.getAccuracy();
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Processing new location: " + location
|
||||
+ ", hasMoved=" + hasMoved
|
||||
+ ", hasBetterAccuracy=" + hasBetterAccuracy);
|
||||
}
|
||||
if (hasMoved || hasBetterAccuracy) {
|
||||
setLocation(location);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_GET_NEW_LOCATION_UPDATE:
|
||||
if (!mNetworkListenerEnabled) {
|
||||
// Don't do anything -- we are still trying to get a
|
||||
// location.
|
||||
return;
|
||||
}
|
||||
if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >=
|
||||
SystemClock.elapsedRealtime()) {
|
||||
// Don't do anything -- it hasn't been long enough
|
||||
// since we last requested an update.
|
||||
return;
|
||||
}
|
||||
|
||||
// Unregister the current location monitor, so we can
|
||||
// register a new one for it to get an immediate update.
|
||||
mNetworkListenerEnabled = false;
|
||||
mLocationManager.removeUpdates(mEmptyLocationListener);
|
||||
|
||||
// Fall through to re-register listener.
|
||||
case MSG_ENABLE_LOCATION_UPDATES:
|
||||
// enable network provider to receive at least location updates for a given
|
||||
// distance.
|
||||
boolean networkLocationEnabled;
|
||||
try {
|
||||
networkLocationEnabled =
|
||||
mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
|
||||
} catch (Exception e) {
|
||||
// we may get IllegalArgumentException if network location provider
|
||||
// does not exist or is not yet installed.
|
||||
networkLocationEnabled = false;
|
||||
}
|
||||
if (!mNetworkListenerEnabled && networkLocationEnabled) {
|
||||
mNetworkListenerEnabled = true;
|
||||
mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
|
||||
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
|
||||
LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
|
||||
|
||||
if (!mDidFirstInit) {
|
||||
mDidFirstInit = true;
|
||||
if (mLocation == null) {
|
||||
retrieveLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enable passive provider to receive updates from location fixes (gps
|
||||
// and network).
|
||||
boolean passiveLocationEnabled;
|
||||
try {
|
||||
passiveLocationEnabled =
|
||||
mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
|
||||
} catch (Exception e) {
|
||||
// we may get IllegalArgumentException if passive location provider
|
||||
// does not exist or is not yet installed.
|
||||
passiveLocationEnabled = false;
|
||||
}
|
||||
|
||||
if (!mPassiveListenerEnabled && passiveLocationEnabled) {
|
||||
mPassiveListenerEnabled = true;
|
||||
mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
|
||||
0, LOCATION_UPDATE_DISTANCE_METER, mLocationListener);
|
||||
}
|
||||
|
||||
if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
|
||||
mLastUpdateInterval *= 1.5;
|
||||
if (mLastUpdateInterval == 0) {
|
||||
mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
|
||||
} else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
|
||||
mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
|
||||
}
|
||||
sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
|
||||
}
|
||||
|
||||
if (!networkLocationEnabled && mLocation == null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Network location unavailable");
|
||||
}
|
||||
retrieveLocation();
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_DO_TWILIGHT_UPDATE:
|
||||
updateTwilightState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void retrieveLocation() {
|
||||
Location location = null;
|
||||
final Iterator<String> providers =
|
||||
mLocationManager.getProviders(new Criteria(), true).iterator();
|
||||
while (providers.hasNext()) {
|
||||
final Location lastKnownLocation =
|
||||
mLocationManager.getLastKnownLocation(providers.next());
|
||||
// pick the most recent location
|
||||
if (location == null || (lastKnownLocation != null &&
|
||||
location.getElapsedRealtimeNanos() <
|
||||
lastKnownLocation.getElapsedRealtimeNanos())) {
|
||||
location = lastKnownLocation;
|
||||
}
|
||||
}
|
||||
|
||||
// In the case there is no location available (e.g. GPS fix or network location
|
||||
// is not available yet), the longitude of the location is estimated using the timezone,
|
||||
// latitude and accuracy are set to get a good average.
|
||||
if (location == null) {
|
||||
Time currentTime = new Time();
|
||||
currentTime.set(System.currentTimeMillis());
|
||||
double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
|
||||
(currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
|
||||
location = new Location("fake");
|
||||
location.setLongitude(lngOffset);
|
||||
location.setLatitude(0);
|
||||
location.setAccuracy(417000.0f);
|
||||
location.setTime(System.currentTimeMillis());
|
||||
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Estimated location from timezone: " + location);
|
||||
}
|
||||
}
|
||||
|
||||
setLocation(location);
|
||||
}
|
||||
|
||||
private void setLocation(Location location) {
|
||||
mLocation = location;
|
||||
updateTwilightState();
|
||||
}
|
||||
|
||||
private void updateTwilightState() {
|
||||
if (mLocation == null) {
|
||||
setTwilightState(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
|
||||
// calculate yesterday's twilight
|
||||
mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
|
||||
mLocation.getLatitude(), mLocation.getLongitude());
|
||||
final long yesterdaySunset = mTwilightCalculator.mSunset;
|
||||
|
||||
// calculate today's twilight
|
||||
mTwilightCalculator.calculateTwilight(now,
|
||||
mLocation.getLatitude(), mLocation.getLongitude());
|
||||
final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT);
|
||||
final long todaySunrise = mTwilightCalculator.mSunrise;
|
||||
final long todaySunset = mTwilightCalculator.mSunset;
|
||||
|
||||
// calculate tomorrow's twilight
|
||||
mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
|
||||
mLocation.getLatitude(), mLocation.getLongitude());
|
||||
final long tomorrowSunrise = mTwilightCalculator.mSunrise;
|
||||
|
||||
// set twilight state
|
||||
TwilightState state = new TwilightState(isNight, yesterdaySunset,
|
||||
todaySunrise, todaySunset, tomorrowSunrise);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Updating twilight state: " + state);
|
||||
}
|
||||
setTwilightState(state);
|
||||
|
||||
// schedule next update
|
||||
long nextUpdate = 0;
|
||||
if (todaySunrise == -1 || todaySunset == -1) {
|
||||
// In the case the day or night never ends the update is scheduled 12 hours later.
|
||||
nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
|
||||
} else {
|
||||
// add some extra time to be on the safe side.
|
||||
nextUpdate += DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
if (now > todaySunset) {
|
||||
nextUpdate += tomorrowSunrise;
|
||||
} else if (now > todaySunrise) {
|
||||
nextUpdate += todaySunset;
|
||||
} else {
|
||||
nextUpdate += todaySunrise;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms");
|
||||
}
|
||||
|
||||
Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
mContext, 0, updateIntent, 0);
|
||||
mAlarmManager.cancel(pendingIntent);
|
||||
mAlarmManager.setExact(AlarmManager.RTC, nextUpdate, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())
|
||||
&& !intent.getBooleanExtra("state", false)) {
|
||||
// Airplane mode is now off!
|
||||
mLocationHandler.requestLocationUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
// Time zone has changed or alarm expired.
|
||||
mLocationHandler.requestTwilightUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
// A LocationListener to initialize the network location provider. The location updates
|
||||
// are handled through the passive location provider.
|
||||
private final LocationListener mEmptyLocationListener = new LocationListener() {
|
||||
public void onLocationChanged(Location location) {
|
||||
}
|
||||
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
};
|
||||
|
||||
private final LocationListener mLocationListener = new LocationListener() {
|
||||
public void onLocationChanged(Location location) {
|
||||
mLocationHandler.processNewLocation(location);
|
||||
}
|
||||
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
};
|
||||
|
||||
public static class TwilightState {
|
||||
private final boolean mIsNight;
|
||||
private final long mYesterdaySunset;
|
||||
private final long mTodaySunrise;
|
||||
private final long mTodaySunset;
|
||||
private final long mTomorrowSunrise;
|
||||
|
||||
TwilightState(boolean isNight,
|
||||
long yesterdaySunset,
|
||||
long todaySunrise, long todaySunset,
|
||||
long tomorrowSunrise) {
|
||||
mIsNight = isNight;
|
||||
mYesterdaySunset = yesterdaySunset;
|
||||
mTodaySunrise = todaySunrise;
|
||||
mTodaySunset = todaySunset;
|
||||
mTomorrowSunrise = tomorrowSunrise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if it is currently night time.
|
||||
*/
|
||||
public boolean isNight() {
|
||||
return mIsNight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase,
|
||||
* or -1 if the sun never sets.
|
||||
*/
|
||||
public long getYesterdaySunset() {
|
||||
return mYesterdaySunset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of today's sunrise in the System.currentTimeMillis() timebase,
|
||||
* or -1 if the sun never rises.
|
||||
*/
|
||||
public long getTodaySunrise() {
|
||||
return mTodaySunrise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of today's sunset in the System.currentTimeMillis() timebase,
|
||||
* or -1 if the sun never sets.
|
||||
*/
|
||||
public long getTodaySunset() {
|
||||
return mTodaySunset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase,
|
||||
* or -1 if the sun never rises.
|
||||
*/
|
||||
public long getTomorrowSunrise() {
|
||||
return mTomorrowSunrise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof TwilightState && equals((TwilightState) o);
|
||||
}
|
||||
|
||||
public boolean equals(TwilightState other) {
|
||||
return other != null
|
||||
&& mIsNight == other.mIsNight
|
||||
&& mYesterdaySunset == other.mYesterdaySunset
|
||||
&& mTodaySunrise == other.mTodaySunrise
|
||||
&& mTodaySunset == other.mTodaySunset
|
||||
&& mTomorrowSunrise == other.mTomorrowSunrise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0; // don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
DateFormat f = DateFormat.getDateTimeInstance();
|
||||
return "{TwilightState: isNight=" + mIsNight
|
||||
+ ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset))
|
||||
+ ", mTodaySunrise=" + f.format(new Date(mTodaySunrise))
|
||||
+ ", mTodaySunset=" + f.format(new Date(mTodaySunset))
|
||||
+ ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise))
|
||||
+ "}";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public interface TwilightListener {
|
||||
void onTwilightStateChanged();
|
||||
}
|
||||
|
||||
private static class TwilightListenerRecord implements Runnable {
|
||||
private final TwilightListener mListener;
|
||||
private final Handler mHandler;
|
||||
|
||||
public TwilightListenerRecord(TwilightListener listener, Handler handler) {
|
||||
mListener = listener;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
public void postUpdate() {
|
||||
mHandler.post(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mListener.onTwilightStateChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user