12416e09649eb6ab767eba458796e126196c77a34Jeff Brown/* 2908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen * Copyright (C) 2016 The Android Open Source Project 32416e09649eb6ab767eba458796e126196c77a34Jeff Brown * 42416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 52416e09649eb6ab767eba458796e126196c77a34Jeff Brown * you may not use this file except in compliance with the License. 62416e09649eb6ab767eba458796e126196c77a34Jeff Brown * You may obtain a copy of the License at 72416e09649eb6ab767eba458796e126196c77a34Jeff Brown * 82416e09649eb6ab767eba458796e126196c77a34Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 92416e09649eb6ab767eba458796e126196c77a34Jeff Brown * 102416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Unless required by applicable law or agreed to in writing, software 112416e09649eb6ab767eba458796e126196c77a34Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 122416e09649eb6ab767eba458796e126196c77a34Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132416e09649eb6ab767eba458796e126196c77a34Jeff Brown * See the License for the specific language governing permissions and 142416e09649eb6ab767eba458796e126196c77a34Jeff Brown * limitations under the License. 152416e09649eb6ab767eba458796e126196c77a34Jeff Brown */ 162416e09649eb6ab767eba458796e126196c77a34Jeff Brown 17182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskipackage com.android.server.twilight; 18182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski 19908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassenimport android.annotation.NonNull; 202416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.app.AlarmManager; 212416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.BroadcastReceiver; 222416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.Context; 232416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.Intent; 242416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.IntentFilter; 25908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassenimport android.icu.impl.CalendarAstronomer; 26908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassenimport android.icu.util.Calendar; 272416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.Location; 282416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.LocationListener; 292416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.LocationManager; 302416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.Bundle; 312416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.Handler; 32908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassenimport android.os.Looper; 332416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.Message; 34908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassenimport android.util.ArrayMap; 352416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.util.Slog; 362416e09649eb6ab767eba458796e126196c77a34Jeff Brown 376384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassenimport com.android.internal.annotations.GuardedBy; 386384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassenimport com.android.server.SystemService; 396384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassen 406384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassenimport java.util.Objects; 412416e09649eb6ab767eba458796e126196c77a34Jeff Brown 422416e09649eb6ab767eba458796e126196c77a34Jeff Brown/** 432416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Figures out whether it's twilight time based on the user's location. 446384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassen * <p> 452416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Used by the UI mode manager and other components to adjust night mode 462416e09649eb6ab767eba458796e126196c77a34Jeff Brown * effects based on sunrise and sunset. 472416e09649eb6ab767eba458796e126196c77a34Jeff Brown */ 48908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassenpublic final class TwilightService extends SystemService 49908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener { 502416e09649eb6ab767eba458796e126196c77a34Jeff Brown 516384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassen private static final String TAG = "TwilightService"; 526384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassen private static final boolean DEBUG = false; 535dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk 54908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private static final int MSG_START_LISTENING = 1; 55908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private static final int MSG_STOP_LISTENING = 2; 565dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk 57908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @GuardedBy("mListeners") 58908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private final ArrayMap<TwilightListener, Handler> mListeners = new ArrayMap<>(); 592416e09649eb6ab767eba458796e126196c77a34Jeff Brown 60908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private final Handler mHandler; 612416e09649eb6ab767eba458796e126196c77a34Jeff Brown 62cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks protected AlarmManager mAlarmManager; 636384878d69d3deb4a7c314f611ee62a634f21411Justin Klaassen private LocationManager mLocationManager; 642416e09649eb6ab767eba458796e126196c77a34Jeff Brown 65908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private boolean mBootCompleted; 66908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private boolean mHasListeners; 67908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 68908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private BroadcastReceiver mTimeChangedReceiver; 69cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks protected Location mLastLocation; 70908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 71908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @GuardedBy("mListeners") 72cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks protected TwilightState mLastTwilightState; 735dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk 74b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown public TwilightService(Context context) { 75b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown super(context); 76908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mHandler = new Handler(Looper.getMainLooper(), this); 77b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown } 78b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown 79182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski @Override 80182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski public void onStart() { 81908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen publishLocalService(TwilightManager.class, new TwilightManager() { 82908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 83908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void registerListener(@NonNull TwilightListener listener, 84908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @NonNull Handler handler) { 85908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen synchronized (mListeners) { 86908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final boolean wasEmpty = mListeners.isEmpty(); 87908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mListeners.put(listener, handler); 88908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 89908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (wasEmpty && !mListeners.isEmpty()) { 90908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mHandler.sendEmptyMessage(MSG_START_LISTENING); 91908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 925dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk } 93908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 945dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk 95908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 96908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void unregisterListener(@NonNull TwilightListener listener) { 97908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen synchronized (mListeners) { 98908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final boolean wasEmpty = mListeners.isEmpty(); 99908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mListeners.remove(listener); 1005dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk 101908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (!wasEmpty && mListeners.isEmpty()) { 102908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mHandler.sendEmptyMessage(MSG_STOP_LISTENING); 103908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 1045dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk } 1055dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk } 1062416e09649eb6ab767eba458796e126196c77a34Jeff Brown 107908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 108908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public TwilightState getLastTwilightState() { 109908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen synchronized (mListeners) { 110908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen return mLastTwilightState; 111908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 112182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski } 113908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen }); 114908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 1152416e09649eb6ab767eba458796e126196c77a34Jeff Brown 116908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 117908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onBootPhase(int phase) { 118908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (phase == PHASE_BOOT_COMPLETED) { 119908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final Context c = getContext(); 120908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mAlarmManager = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE); 121908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLocationManager = (LocationManager) c.getSystemService(Context.LOCATION_SERVICE); 122182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski 123908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mBootCompleted = true; 124908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mHasListeners) { 125908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen startListening(); 1262416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 1272416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 128908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 1292416e09649eb6ab767eba458796e126196c77a34Jeff Brown 130908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 131908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public boolean handleMessage(Message msg) { 132908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen switch (msg.what) { 133908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen case MSG_START_LISTENING: 134908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (!mHasListeners) { 135908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mHasListeners = true; 136908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mBootCompleted) { 137908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen startListening(); 1385dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk } 1392416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 140908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen return true; 141908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen case MSG_STOP_LISTENING: 142908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mHasListeners) { 143908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mHasListeners = false; 144908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mBootCompleted) { 145908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen stopListening(); 146908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 1472416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 148908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen return true; 1492416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 150908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen return false; 151908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 1522416e09649eb6ab767eba458796e126196c77a34Jeff Brown 153908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private void startListening() { 154ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen Slog.d(TAG, "startListening"); 155908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 156908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Start listening for location updates (default: low power, max 1h, min 10m). 157908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLocationManager.requestLocationUpdates( 158908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen null /* default */, this, Looper.getMainLooper()); 159908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 160908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Request the device's location immediately if a previous location isn't available. 161908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mLocationManager.getLastLocation() == null) { 162908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { 163908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLocationManager.requestSingleUpdate( 164908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen LocationManager.NETWORK_PROVIDER, this, Looper.getMainLooper()); 165908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } else if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { 166908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLocationManager.requestSingleUpdate( 167908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen LocationManager.GPS_PROVIDER, this, Looper.getMainLooper()); 168908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 1692416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 1702416e09649eb6ab767eba458796e126196c77a34Jeff Brown 171908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Update whenever the system clock is changed. 172908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mTimeChangedReceiver == null) { 173908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mTimeChangedReceiver = new BroadcastReceiver() { 174908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 175908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onReceive(Context context, Intent intent) { 176ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen Slog.d(TAG, "onReceive: " + intent); 177908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen updateTwilightState(); 178908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 179908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen }; 1802416e09649eb6ab767eba458796e126196c77a34Jeff Brown 181908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED); 182908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 183908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen getContext().registerReceiver(mTimeChangedReceiver, intentFilter); 1842416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 1852416e09649eb6ab767eba458796e126196c77a34Jeff Brown 186908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Force an update now that we have listeners registered. 187908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen updateTwilightState(); 1882416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 1892416e09649eb6ab767eba458796e126196c77a34Jeff Brown 190908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private void stopListening() { 191ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen Slog.d(TAG, "stopListening"); 1922416e09649eb6ab767eba458796e126196c77a34Jeff Brown 193908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mTimeChangedReceiver != null) { 194908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen getContext().unregisterReceiver(mTimeChangedReceiver); 195908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mTimeChangedReceiver = null; 1962416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 1972416e09649eb6ab767eba458796e126196c77a34Jeff Brown 198908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (mLastTwilightState != null) { 199908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mAlarmManager.cancel(this); 2005dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk } 2015dbd4aad809e6fec51df62280bcc1bfe05cc7df5Jason Monk 202908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLocationManager.removeUpdates(this); 203908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLastLocation = null; 204908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 2052416e09649eb6ab767eba458796e126196c77a34Jeff Brown 206908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private void updateTwilightState() { 207908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Calculate the twilight state based on the current time and location. 208908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final long currentTimeMillis = System.currentTimeMillis(); 209908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final Location location = mLastLocation != null ? mLastLocation 210908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen : mLocationManager.getLastLocation(); 211908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final TwilightState state = calculateTwilightState(location, currentTimeMillis); 212908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (DEBUG) { 213908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen Slog.d(TAG, "updateTwilightState: " + state); 2142416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 2152416e09649eb6ab767eba458796e126196c77a34Jeff Brown 216908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Notify listeners if the state has changed. 217908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen synchronized (mListeners) { 218908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (!Objects.equals(mLastTwilightState, state)) { 219908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mLastTwilightState = state; 220908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 221908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen for (int i = mListeners.size() - 1; i >= 0; --i) { 222908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final TwilightListener listener = mListeners.keyAt(i); 223908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final Handler handler = mListeners.valueAt(i); 224908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen handler.post(new Runnable() { 225908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 226908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void run() { 227908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen listener.onTwilightStateChanged(state); 2282416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 229908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen }); 2302416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 2312416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 2322416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 2332416e09649eb6ab767eba458796e126196c77a34Jeff Brown 234908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen // Schedule an alarm to update the state at the next sunrise or sunset. 235908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (state != null) { 236908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final long triggerAtMillis = state.isNight() 237908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen ? state.sunriseTimeMillis() : state.sunsetTimeMillis(); 238908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen mAlarmManager.setExact(AlarmManager.RTC, triggerAtMillis, TAG, this, mHandler); 2392416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 240182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski } 2412416e09649eb6ab767eba458796e126196c77a34Jeff Brown 242908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 243908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onAlarm() { 244ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen Slog.d(TAG, "onAlarm"); 245908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen updateTwilightState(); 246908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 2472416e09649eb6ab767eba458796e126196c77a34Jeff Brown 248908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 249908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onLocationChanged(Location location) { 250cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks // Location providers may erroneously return (0.0, 0.0) when they fail to determine the 251cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks // device's location. These location updates can be safely ignored since the chance of a 252cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks // user actually being at these coordinates is quite low. 253cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks if (location != null 254cc2801296dd1829196fcaa7b4018e372121e36e5Christine Franks && !(location.getLongitude() == 0.0 && location.getLatitude() == 0.0)) { 255ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen Slog.d(TAG, "onLocationChanged:" 256ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen + " provider=" + location.getProvider() 257ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen + " accuracy=" + location.getAccuracy() 258ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen + " time=" + location.getTime()); 259ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen mLastLocation = location; 260ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen updateTwilightState(); 261ec8837ae2670fbca115c566f8871e66bfa72917bJustin Klaassen } 262908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 2632416e09649eb6ab767eba458796e126196c77a34Jeff Brown 264908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 265908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onStatusChanged(String provider, int status, Bundle extras) { 266908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 2672416e09649eb6ab767eba458796e126196c77a34Jeff Brown 268908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 269908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onProviderEnabled(String provider) { 270908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 2712416e09649eb6ab767eba458796e126196c77a34Jeff Brown 272908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen @Override 273908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen public void onProviderDisabled(String provider) { 274908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 2752416e09649eb6ab767eba458796e126196c77a34Jeff Brown 276908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen /** 277908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen * Calculates the twilight state for a specific location and time. 278908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen * 279908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen * @param location the location to use 280908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen * @param timeMillis the reference time to use 281908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen * @return the calculated {@link TwilightState}, or {@code null} if location is {@code null} 282908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen */ 283908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen private static TwilightState calculateTwilightState(Location location, long timeMillis) { 284908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (location == null) { 285908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen return null; 2862416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 2872416e09649eb6ab767eba458796e126196c77a34Jeff Brown 288908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final CalendarAstronomer ca = new CalendarAstronomer( 289908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen location.getLongitude(), location.getLatitude()); 290908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 291908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen final Calendar noon = Calendar.getInstance(); 292908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.setTimeInMillis(timeMillis); 293908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.set(Calendar.HOUR_OF_DAY, 12); 294908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.set(Calendar.MINUTE, 0); 295908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.set(Calendar.SECOND, 0); 296908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.set(Calendar.MILLISECOND, 0); 297908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen ca.setTime(noon.getTimeInMillis()); 298908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 299908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen long sunriseTimeMillis = ca.getSunRiseSet(true /* rise */); 300908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen long sunsetTimeMillis = ca.getSunRiseSet(false /* rise */); 301908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen 302908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen if (sunsetTimeMillis < timeMillis) { 303908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.add(Calendar.DATE, 1); 304908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen ca.setTime(noon.getTimeInMillis()); 305908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen sunriseTimeMillis = ca.getSunRiseSet(true /* rise */); 306908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } else if (sunriseTimeMillis > timeMillis) { 307908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen noon.add(Calendar.DATE, -1); 308908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen ca.setTime(noon.getTimeInMillis()); 309908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen sunsetTimeMillis = ca.getSunRiseSet(false /* rise */); 3102416e09649eb6ab767eba458796e126196c77a34Jeff Brown } 3112416e09649eb6ab767eba458796e126196c77a34Jeff Brown 312908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen return new TwilightState(sunriseTimeMillis, sunsetTimeMillis); 313908b86c796443ba4ec55c669e8a0297fc80574a6Justin Klaassen } 3142416e09649eb6ab767eba458796e126196c77a34Jeff Brown} 315