12416e09649eb6ab767eba458796e126196c77a34Jeff Brown/*
22416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Copyright (C) 2012 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
172416e09649eb6ab767eba458796e126196c77a34Jeff Brownpackage com.android.server;
182416e09649eb6ab767eba458796e126196c77a34Jeff Brown
192416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.app.AlarmManager;
202416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.app.PendingIntent;
212416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.BroadcastReceiver;
222416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.Context;
232416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.Intent;
242416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.content.IntentFilter;
252416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.Criteria;
262416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.Location;
272416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.LocationListener;
282416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.location.LocationManager;
292416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.Bundle;
302416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.Handler;
312416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.Message;
322416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.os.SystemClock;
332416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.text.format.DateUtils;
342416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.text.format.Time;
352416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport android.util.Slog;
362416e09649eb6ab767eba458796e126196c77a34Jeff Brown
372416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport java.text.DateFormat;
382416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport java.util.ArrayList;
392416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport java.util.Date;
402416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport java.util.Iterator;
412416e09649eb6ab767eba458796e126196c77a34Jeff Brown
422416e09649eb6ab767eba458796e126196c77a34Jeff Brownimport libcore.util.Objects;
432416e09649eb6ab767eba458796e126196c77a34Jeff Brown
442416e09649eb6ab767eba458796e126196c77a34Jeff Brown/**
452416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Figures out whether it's twilight time based on the user's location.
462416e09649eb6ab767eba458796e126196c77a34Jeff Brown *
472416e09649eb6ab767eba458796e126196c77a34Jeff Brown * Used by the UI mode manager and other components to adjust night mode
482416e09649eb6ab767eba458796e126196c77a34Jeff Brown * effects based on sunrise and sunset.
492416e09649eb6ab767eba458796e126196c77a34Jeff Brown */
502416e09649eb6ab767eba458796e126196c77a34Jeff Brownpublic final class TwilightService {
512416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private static final String TAG = "TwilightService";
522416e09649eb6ab767eba458796e126196c77a34Jeff Brown
53b5b710a4851db6c7e247429b0551a523c4d33da6Jeff Brown    private static final boolean DEBUG = false;
542416e09649eb6ab767eba458796e126196c77a34Jeff Brown
552416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private static final String ACTION_UPDATE_TWILIGHT_STATE =
562416e09649eb6ab767eba458796e126196c77a34Jeff Brown            "com.android.server.action.UPDATE_TWILIGHT_STATE";
572416e09649eb6ab767eba458796e126196c77a34Jeff Brown
582416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final Context mContext;
592416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final AlarmManager mAlarmManager;
602416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final LocationManager mLocationManager;
612416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final LocationHandler mLocationHandler;
622416e09649eb6ab767eba458796e126196c77a34Jeff Brown
632416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final Object mLock = new Object();
642416e09649eb6ab767eba458796e126196c77a34Jeff Brown
652416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final ArrayList<TwilightListenerRecord> mListeners =
662416e09649eb6ab767eba458796e126196c77a34Jeff Brown            new ArrayList<TwilightListenerRecord>();
672416e09649eb6ab767eba458796e126196c77a34Jeff Brown
682416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private boolean mSystemReady;
692416e09649eb6ab767eba458796e126196c77a34Jeff Brown
702416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private TwilightState mTwilightState;
712416e09649eb6ab767eba458796e126196c77a34Jeff Brown
722416e09649eb6ab767eba458796e126196c77a34Jeff Brown    public TwilightService(Context context) {
732416e09649eb6ab767eba458796e126196c77a34Jeff Brown        mContext = context;
742416e09649eb6ab767eba458796e126196c77a34Jeff Brown
752416e09649eb6ab767eba458796e126196c77a34Jeff Brown        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
762416e09649eb6ab767eba458796e126196c77a34Jeff Brown        mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
772416e09649eb6ab767eba458796e126196c77a34Jeff Brown        mLocationHandler = new LocationHandler();
782416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
792416e09649eb6ab767eba458796e126196c77a34Jeff Brown
802416e09649eb6ab767eba458796e126196c77a34Jeff Brown    void systemReady() {
812416e09649eb6ab767eba458796e126196c77a34Jeff Brown        synchronized (mLock) {
822416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mSystemReady = true;
832416e09649eb6ab767eba458796e126196c77a34Jeff Brown
842416e09649eb6ab767eba458796e126196c77a34Jeff Brown            IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
852416e09649eb6ab767eba458796e126196c77a34Jeff Brown            filter.addAction(Intent.ACTION_TIME_CHANGED);
862416e09649eb6ab767eba458796e126196c77a34Jeff Brown            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
872416e09649eb6ab767eba458796e126196c77a34Jeff Brown            filter.addAction(ACTION_UPDATE_TWILIGHT_STATE);
882416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mContext.registerReceiver(mUpdateLocationReceiver, filter);
892416e09649eb6ab767eba458796e126196c77a34Jeff Brown
902416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (!mListeners.isEmpty()) {
912416e09649eb6ab767eba458796e126196c77a34Jeff Brown                mLocationHandler.enableLocationUpdates();
922416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
932416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
942416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
952416e09649eb6ab767eba458796e126196c77a34Jeff Brown
962416e09649eb6ab767eba458796e126196c77a34Jeff Brown    /**
972416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * Gets the current twilight state.
982416e09649eb6ab767eba458796e126196c77a34Jeff Brown     *
992416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * @return The current twilight state, or null if no information is available.
1002416e09649eb6ab767eba458796e126196c77a34Jeff Brown     */
1012416e09649eb6ab767eba458796e126196c77a34Jeff Brown    public TwilightState getCurrentState() {
1022416e09649eb6ab767eba458796e126196c77a34Jeff Brown        synchronized (mLock) {
1032416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return mTwilightState;
1042416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1052416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
1062416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1072416e09649eb6ab767eba458796e126196c77a34Jeff Brown    /**
1082416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * Listens for twilight time.
1092416e09649eb6ab767eba458796e126196c77a34Jeff Brown     *
1102416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * @param listener The listener.
1112416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * @param handler The handler on which to post calls into the listener.
1122416e09649eb6ab767eba458796e126196c77a34Jeff Brown     */
1132416e09649eb6ab767eba458796e126196c77a34Jeff Brown    public void registerListener(TwilightListener listener, Handler handler) {
1142416e09649eb6ab767eba458796e126196c77a34Jeff Brown        synchronized (mLock) {
1152416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mListeners.add(new TwilightListenerRecord(listener, handler));
1162416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1172416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (mSystemReady && mListeners.size() == 1) {
1182416e09649eb6ab767eba458796e126196c77a34Jeff Brown                mLocationHandler.enableLocationUpdates();
1192416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
1202416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1212416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
1222416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1232416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private void setTwilightState(TwilightState state) {
1242416e09649eb6ab767eba458796e126196c77a34Jeff Brown        synchronized (mLock) {
1252416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (!Objects.equal(mTwilightState, state)) {
1262416e09649eb6ab767eba458796e126196c77a34Jeff Brown                if (DEBUG) {
1272416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    Slog.d(TAG, "Twilight state changed: " + state);
1282416e09649eb6ab767eba458796e126196c77a34Jeff Brown                }
1292416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1302416e09649eb6ab767eba458796e126196c77a34Jeff Brown                mTwilightState = state;
1312416e09649eb6ab767eba458796e126196c77a34Jeff Brown                int count = mListeners.size();
1322416e09649eb6ab767eba458796e126196c77a34Jeff Brown                for (int i = 0; i < count; i++) {
1332416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mListeners.get(i).post();
1342416e09649eb6ab767eba458796e126196c77a34Jeff Brown                }
1352416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
1362416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1372416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
1382416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1392416e09649eb6ab767eba458796e126196c77a34Jeff Brown    // The user has moved if the accuracy circles of the two locations don't overlap.
1402416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private static boolean hasMoved(Location from, Location to) {
1412416e09649eb6ab767eba458796e126196c77a34Jeff Brown        if (to == null) {
1422416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return false;
1432416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1442416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1452416e09649eb6ab767eba458796e126196c77a34Jeff Brown        if (from == null) {
1462416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return true;
1472416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1482416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1492416e09649eb6ab767eba458796e126196c77a34Jeff Brown        // if new location is older than the current one, the device hasn't moved.
1504118012da9a22694b3353040a485f8cdc27e2f17Philip Milne        if (to.getElapsedRealtimeNanos() < from.getElapsedRealtimeNanos()) {
1512416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return false;
1522416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1532416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1542416e09649eb6ab767eba458796e126196c77a34Jeff Brown        // Get the distance between the two points.
1552416e09649eb6ab767eba458796e126196c77a34Jeff Brown        float distance = from.distanceTo(to);
1562416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1572416e09649eb6ab767eba458796e126196c77a34Jeff Brown        // Get the total accuracy radius for both locations.
1582416e09649eb6ab767eba458796e126196c77a34Jeff Brown        float totalAccuracy = from.getAccuracy() + to.getAccuracy();
1592416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1602416e09649eb6ab767eba458796e126196c77a34Jeff Brown        // If the distance is greater than the combined accuracy of the two
1612416e09649eb6ab767eba458796e126196c77a34Jeff Brown        // points then they can't overlap and hence the user has moved.
1622416e09649eb6ab767eba458796e126196c77a34Jeff Brown        return distance >= totalAccuracy;
1632416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
1642416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1652416e09649eb6ab767eba458796e126196c77a34Jeff Brown    /**
1662416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * Describes whether it is day or night.
1672416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * This object is immutable.
1682416e09649eb6ab767eba458796e126196c77a34Jeff Brown     */
1692416e09649eb6ab767eba458796e126196c77a34Jeff Brown    public static final class TwilightState {
1702416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final boolean mIsNight;
1712416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final long mYesterdaySunset;
1722416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final long mTodaySunrise;
1732416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final long mTodaySunset;
1742416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final long mTomorrowSunrise;
1752416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1762416e09649eb6ab767eba458796e126196c77a34Jeff Brown        TwilightState(boolean isNight,
1772416e09649eb6ab767eba458796e126196c77a34Jeff Brown                long yesterdaySunset,
1782416e09649eb6ab767eba458796e126196c77a34Jeff Brown                long todaySunrise, long todaySunset,
1792416e09649eb6ab767eba458796e126196c77a34Jeff Brown                long tomorrowSunrise) {
1802416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mIsNight = isNight;
1812416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mYesterdaySunset = yesterdaySunset;
1822416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mTodaySunrise = todaySunrise;
1832416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mTodaySunset = todaySunset;
1842416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mTomorrowSunrise = tomorrowSunrise;
1852416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1862416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1872416e09649eb6ab767eba458796e126196c77a34Jeff Brown        /**
1882416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * Returns true if it is currently night time.
1892416e09649eb6ab767eba458796e126196c77a34Jeff Brown         */
1902416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public boolean isNight() {
1912416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return mIsNight;
1922416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
1932416e09649eb6ab767eba458796e126196c77a34Jeff Brown
1942416e09649eb6ab767eba458796e126196c77a34Jeff Brown        /**
1952416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase,
1962416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * or -1 if the sun never sets.
1972416e09649eb6ab767eba458796e126196c77a34Jeff Brown         */
1982416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public long getYesterdaySunset() {
1992416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return mYesterdaySunset;
2002416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2012416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2022416e09649eb6ab767eba458796e126196c77a34Jeff Brown        /**
2032416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * Returns the time of today's sunrise in the System.currentTimeMillis() timebase,
2042416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * or -1 if the sun never rises.
2052416e09649eb6ab767eba458796e126196c77a34Jeff Brown         */
2062416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public long getTodaySunrise() {
2072416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return mTodaySunrise;
2082416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2092416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2102416e09649eb6ab767eba458796e126196c77a34Jeff Brown        /**
2112416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * Returns the time of today's sunset in the System.currentTimeMillis() timebase,
2122416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * or -1 if the sun never sets.
2132416e09649eb6ab767eba458796e126196c77a34Jeff Brown         */
2142416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public long getTodaySunset() {
2152416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return mTodaySunset;
2162416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2172416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2182416e09649eb6ab767eba458796e126196c77a34Jeff Brown        /**
2192416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase,
2202416e09649eb6ab767eba458796e126196c77a34Jeff Brown         * or -1 if the sun never rises.
2212416e09649eb6ab767eba458796e126196c77a34Jeff Brown         */
2222416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public long getTomorrowSunrise() {
2232416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return mTomorrowSunrise;
2242416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2252416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2262416e09649eb6ab767eba458796e126196c77a34Jeff Brown        @Override
2272416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public boolean equals(Object o) {
2282416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return o instanceof TwilightState && equals((TwilightState)o);
2292416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2302416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2312416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public boolean equals(TwilightState other) {
2322416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return other != null
2332416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    && mIsNight == other.mIsNight
2342416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    && mYesterdaySunset == other.mYesterdaySunset
2352416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    && mTodaySunrise == other.mTodaySunrise
2362416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    && mTodaySunset == other.mTodaySunset
2372416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    && mTomorrowSunrise == other.mTomorrowSunrise;
2382416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2392416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2402416e09649eb6ab767eba458796e126196c77a34Jeff Brown        @Override
2412416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public int hashCode() {
2422416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return 0; // don't care
2432416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2442416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2452416e09649eb6ab767eba458796e126196c77a34Jeff Brown        @Override
2462416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public String toString() {
2472416e09649eb6ab767eba458796e126196c77a34Jeff Brown            DateFormat f = DateFormat.getDateTimeInstance();
2482416e09649eb6ab767eba458796e126196c77a34Jeff Brown            return "{TwilightState: isNight=" + mIsNight
2492416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset))
2502416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise))
2512416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    + ", mTodaySunset=" + f.format(new Date(mTodaySunset))
2522416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise))
2532416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    + "}";
2542416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2552416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
2562416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2572416e09649eb6ab767eba458796e126196c77a34Jeff Brown    /**
2582416e09649eb6ab767eba458796e126196c77a34Jeff Brown     * Listener for changes in twilight state.
2592416e09649eb6ab767eba458796e126196c77a34Jeff Brown     */
2602416e09649eb6ab767eba458796e126196c77a34Jeff Brown    public interface TwilightListener {
2612416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onTwilightStateChanged();
2622416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
2632416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2642416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private static final class TwilightListenerRecord implements Runnable {
2652416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final TwilightListener mListener;
2662416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final Handler mHandler;
2672416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2682416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public TwilightListenerRecord(TwilightListener listener, Handler handler) {
2692416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mListener = listener;
2702416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mHandler = handler;
2712416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2722416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2732416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void post() {
2742416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mHandler.post(this);
2752416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2762416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2772416e09649eb6ab767eba458796e126196c77a34Jeff Brown        @Override
2782416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void run() {
2792416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mListener.onTwilightStateChanged();
2802416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
2812416e09649eb6ab767eba458796e126196c77a34Jeff Brown    }
2822416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2832416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final class LocationHandler extends Handler {
2842416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
2852416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
2862416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final int MSG_PROCESS_NEW_LOCATION = 3;
2872416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final int MSG_DO_TWILIGHT_UPDATE = 4;
2882416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2892416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
2902416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
2912416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
2922416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
2932416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX =
2942416e09649eb6ab767eba458796e126196c77a34Jeff Brown                15 * DateUtils.MINUTE_IN_MILLIS;
2952416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private static final double FACTOR_GMT_OFFSET_LONGITUDE =
2962416e09649eb6ab767eba458796e126196c77a34Jeff Brown                1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
2972416e09649eb6ab767eba458796e126196c77a34Jeff Brown
2982416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private boolean mPassiveListenerEnabled;
2992416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private boolean mNetworkListenerEnabled;
3002416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private boolean mDidFirstInit;
3012416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
3022416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private long mLastUpdateInterval;
3032416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private Location mLocation;
3042416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private final TwilightCalculator mTwilightCalculator = new TwilightCalculator();
3052416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3062416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void processNewLocation(Location location) {
3072416e09649eb6ab767eba458796e126196c77a34Jeff Brown            Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location);
3082416e09649eb6ab767eba458796e126196c77a34Jeff Brown            sendMessage(msg);
3092416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
3102416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3112416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void enableLocationUpdates() {
3122416e09649eb6ab767eba458796e126196c77a34Jeff Brown            sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
3132416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
3142416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3152416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void requestLocationUpdate() {
3162416e09649eb6ab767eba458796e126196c77a34Jeff Brown            sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
3172416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
3182416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3192416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void requestTwilightUpdate() {
3202416e09649eb6ab767eba458796e126196c77a34Jeff Brown            sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE);
3212416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
3222416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3232416e09649eb6ab767eba458796e126196c77a34Jeff Brown        @Override
3242416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void handleMessage(Message msg) {
3252416e09649eb6ab767eba458796e126196c77a34Jeff Brown            switch (msg.what) {
3262416e09649eb6ab767eba458796e126196c77a34Jeff Brown                case MSG_PROCESS_NEW_LOCATION: {
3272416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    final Location location = (Location)msg.obj;
3282416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    final boolean hasMoved = hasMoved(mLocation, location);
3292416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    final boolean hasBetterAccuracy = mLocation == null
3302416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            || location.getAccuracy() < mLocation.getAccuracy();
3312416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if (DEBUG) {
3322416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        Slog.d(TAG, "Processing new location: " + location
3332416e09649eb6ab767eba458796e126196c77a34Jeff Brown                               + ", hasMoved=" + hasMoved
3342416e09649eb6ab767eba458796e126196c77a34Jeff Brown                               + ", hasBetterAccuracy=" + hasBetterAccuracy);
3352416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3362416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if (hasMoved || hasBetterAccuracy) {
3372416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        setLocation(location);
3382416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3392416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    break;
3402416e09649eb6ab767eba458796e126196c77a34Jeff Brown                }
3412416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3422416e09649eb6ab767eba458796e126196c77a34Jeff Brown                case MSG_GET_NEW_LOCATION_UPDATE:
3432416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if (!mNetworkListenerEnabled) {
3442416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // Don't do anything -- we are still trying to get a
3452416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // location.
3462416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        return;
3472416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3482416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >=
3492416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            SystemClock.elapsedRealtime()) {
3502416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // Don't do anything -- it hasn't been long enough
3512416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // since we last requested an update.
3522416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        return;
3532416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3542416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3552416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // Unregister the current location monitor, so we can
3562416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // register a new one for it to get an immediate update.
3572416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mNetworkListenerEnabled = false;
3582416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mLocationManager.removeUpdates(mEmptyLocationListener);
3592416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3602416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // Fall through to re-register listener.
3612416e09649eb6ab767eba458796e126196c77a34Jeff Brown                case MSG_ENABLE_LOCATION_UPDATES:
3622416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // enable network provider to receive at least location updates for a given
3632416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // distance.
3642416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    boolean networkLocationEnabled;
3652416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    try {
3662416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        networkLocationEnabled =
3672416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
3682416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    } catch (Exception e) {
3692416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // we may get IllegalArgumentException if network location provider
3702416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // does not exist or is not yet installed.
3712416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        networkLocationEnabled = false;
3722416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3732416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if (!mNetworkListenerEnabled && networkLocationEnabled) {
3742416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mNetworkListenerEnabled = true;
3752416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
3762416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
3772416e09649eb6ab767eba458796e126196c77a34Jeff Brown                                LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
3782416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3792416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        if (!mDidFirstInit) {
3802416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            mDidFirstInit = true;
3812416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            if (mLocation == null) {
3822416e09649eb6ab767eba458796e126196c77a34Jeff Brown                                retrieveLocation();
3832416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            }
3842416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        }
3852416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3862416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3872416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // enable passive provider to receive updates from location fixes (gps
3882416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    // and network).
3892416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    boolean passiveLocationEnabled;
3902416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    try {
3912416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        passiveLocationEnabled =
3922416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
3932416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    } catch (Exception e) {
3942416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // we may get IllegalArgumentException if passive location provider
3952416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        // does not exist or is not yet installed.
3962416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        passiveLocationEnabled = false;
3972416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
3982416e09649eb6ab767eba458796e126196c77a34Jeff Brown
3992416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if (!mPassiveListenerEnabled && passiveLocationEnabled) {
4002416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mPassiveListenerEnabled = true;
4012416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
4022416e09649eb6ab767eba458796e126196c77a34Jeff Brown                                0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
4032416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
4042416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4052416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
4062416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mLastUpdateInterval *= 1.5;
4072416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        if (mLastUpdateInterval == 0) {
4082416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
4092416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        } else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
4102416e09649eb6ab767eba458796e126196c77a34Jeff Brown                            mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
4112416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        }
4122416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
4132416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    }
4142416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    break;
4152416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4162416e09649eb6ab767eba458796e126196c77a34Jeff Brown                case MSG_DO_TWILIGHT_UPDATE:
4172416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    updateTwilightState();
4182416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    break;
4192416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
4202416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
4212416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4222416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private void retrieveLocation() {
4232416e09649eb6ab767eba458796e126196c77a34Jeff Brown            Location location = null;
4242416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final Iterator<String> providers =
4252416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mLocationManager.getProviders(new Criteria(), true).iterator();
4262416e09649eb6ab767eba458796e126196c77a34Jeff Brown            while (providers.hasNext()) {
4272416e09649eb6ab767eba458796e126196c77a34Jeff Brown                final Location lastKnownLocation =
4282416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        mLocationManager.getLastKnownLocation(providers.next());
4292416e09649eb6ab767eba458796e126196c77a34Jeff Brown                // pick the most recent location
4302416e09649eb6ab767eba458796e126196c77a34Jeff Brown                if (location == null || (lastKnownLocation != null &&
4314118012da9a22694b3353040a485f8cdc27e2f17Philip Milne                        location.getElapsedRealtimeNanos() <
4324118012da9a22694b3353040a485f8cdc27e2f17Philip Milne                        lastKnownLocation.getElapsedRealtimeNanos())) {
4332416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    location = lastKnownLocation;
4342416e09649eb6ab767eba458796e126196c77a34Jeff Brown                }
4352416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
4362416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4372416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // In the case there is no location available (e.g. GPS fix or network location
4382416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // is not available yet), the longitude of the location is estimated using the timezone,
4392416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // latitude and accuracy are set to get a good average.
4402416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (location == null) {
4412416e09649eb6ab767eba458796e126196c77a34Jeff Brown                Time currentTime = new Time();
4422416e09649eb6ab767eba458796e126196c77a34Jeff Brown                currentTime.set(System.currentTimeMillis());
4432416e09649eb6ab767eba458796e126196c77a34Jeff Brown                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
4442416e09649eb6ab767eba458796e126196c77a34Jeff Brown                        (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
4452416e09649eb6ab767eba458796e126196c77a34Jeff Brown                location = new Location("fake");
4462416e09649eb6ab767eba458796e126196c77a34Jeff Brown                location.setLongitude(lngOffset);
4472416e09649eb6ab767eba458796e126196c77a34Jeff Brown                location.setLatitude(0);
4482416e09649eb6ab767eba458796e126196c77a34Jeff Brown                location.setAccuracy(417000.0f);
4492416e09649eb6ab767eba458796e126196c77a34Jeff Brown                location.setTime(System.currentTimeMillis());
4504118012da9a22694b3353040a485f8cdc27e2f17Philip Milne                location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
4512416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4522416e09649eb6ab767eba458796e126196c77a34Jeff Brown                if (DEBUG) {
4532416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    Slog.d(TAG, "Estimated location from timezone: " + location);
4542416e09649eb6ab767eba458796e126196c77a34Jeff Brown                }
4552416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
4562416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4572416e09649eb6ab767eba458796e126196c77a34Jeff Brown            setLocation(location);
4582416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
4592416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4602416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private void setLocation(Location location) {
4612416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mLocation = location;
4622416e09649eb6ab767eba458796e126196c77a34Jeff Brown            updateTwilightState();
4632416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
4642416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4652416e09649eb6ab767eba458796e126196c77a34Jeff Brown        private void updateTwilightState() {
466b5b710a4851db6c7e247429b0551a523c4d33da6Jeff Brown            if (mLocation == null) {
467b5b710a4851db6c7e247429b0551a523c4d33da6Jeff Brown                setTwilightState(null);
468b5b710a4851db6c7e247429b0551a523c4d33da6Jeff Brown                return;
469b5b710a4851db6c7e247429b0551a523c4d33da6Jeff Brown            }
470b5b710a4851db6c7e247429b0551a523c4d33da6Jeff Brown
4712416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final long now = System.currentTimeMillis();
4722416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4732416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // calculate yesterday's twilight
4742416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
4752416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mLocation.getLatitude(), mLocation.getLongitude());
4762416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final long yesterdaySunset = mTwilightCalculator.mSunset;
4772416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4782416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // calculate today's twilight
4792416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mTwilightCalculator.calculateTwilight(now,
4802416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mLocation.getLatitude(), mLocation.getLongitude());
4812416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT);
4822416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final long todaySunrise = mTwilightCalculator.mSunrise;
4832416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final long todaySunset = mTwilightCalculator.mSunset;
4842416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4852416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // calculate tomorrow's twilight
4862416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
4872416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    mLocation.getLatitude(), mLocation.getLongitude());
4882416e09649eb6ab767eba458796e126196c77a34Jeff Brown            final long tomorrowSunrise = mTwilightCalculator.mSunrise;
4892416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4902416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // set twilight state
4912416e09649eb6ab767eba458796e126196c77a34Jeff Brown            TwilightState state = new TwilightState(isNight, yesterdaySunset,
4922416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    todaySunrise, todaySunset, tomorrowSunrise);
4932416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (DEBUG) {
4942416e09649eb6ab767eba458796e126196c77a34Jeff Brown                Slog.d(TAG, "Updating twilight state: " + state);
4952416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
4962416e09649eb6ab767eba458796e126196c77a34Jeff Brown            setTwilightState(state);
4972416e09649eb6ab767eba458796e126196c77a34Jeff Brown
4982416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // schedule next update
4992416e09649eb6ab767eba458796e126196c77a34Jeff Brown            long nextUpdate = 0;
5002416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (todaySunrise == -1 || todaySunset == -1) {
5012416e09649eb6ab767eba458796e126196c77a34Jeff Brown                // In the case the day or night never ends the update is scheduled 12 hours later.
5022416e09649eb6ab767eba458796e126196c77a34Jeff Brown                nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
5032416e09649eb6ab767eba458796e126196c77a34Jeff Brown            } else {
5042416e09649eb6ab767eba458796e126196c77a34Jeff Brown                // add some extra time to be on the safe side.
5052416e09649eb6ab767eba458796e126196c77a34Jeff Brown                nextUpdate += DateUtils.MINUTE_IN_MILLIS;
5062416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5072416e09649eb6ab767eba458796e126196c77a34Jeff Brown                if (now > todaySunset) {
5082416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    nextUpdate += tomorrowSunrise;
5092416e09649eb6ab767eba458796e126196c77a34Jeff Brown                } else if (now > todaySunrise) {
5102416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    nextUpdate += todaySunset;
5112416e09649eb6ab767eba458796e126196c77a34Jeff Brown                } else {
5122416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    nextUpdate += todaySunrise;
5132416e09649eb6ab767eba458796e126196c77a34Jeff Brown                }
5142416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
5152416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5162416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (DEBUG) {
5172416e09649eb6ab767eba458796e126196c77a34Jeff Brown                Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms");
5182416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
5192416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5202416e09649eb6ab767eba458796e126196c77a34Jeff Brown            Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
5212416e09649eb6ab767eba458796e126196c77a34Jeff Brown            PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
5222416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mAlarmManager.cancel(pendingIntent);
5232416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
5242416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5252416e09649eb6ab767eba458796e126196c77a34Jeff Brown    };
5262416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5272416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
5282416e09649eb6ab767eba458796e126196c77a34Jeff Brown        @Override
5292416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onReceive(Context context, Intent intent) {
5302416e09649eb6ab767eba458796e126196c77a34Jeff Brown            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())
5312416e09649eb6ab767eba458796e126196c77a34Jeff Brown                    && !intent.getBooleanExtra("state", false)) {
5322416e09649eb6ab767eba458796e126196c77a34Jeff Brown                // Airplane mode is now off!
5332416e09649eb6ab767eba458796e126196c77a34Jeff Brown                mLocationHandler.requestLocationUpdate();
5342416e09649eb6ab767eba458796e126196c77a34Jeff Brown                return;
5352416e09649eb6ab767eba458796e126196c77a34Jeff Brown            }
5362416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5372416e09649eb6ab767eba458796e126196c77a34Jeff Brown            // Time zone has changed or alarm expired.
5382416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mLocationHandler.requestTwilightUpdate();
5392416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5402416e09649eb6ab767eba458796e126196c77a34Jeff Brown    };
5412416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5422416e09649eb6ab767eba458796e126196c77a34Jeff Brown    // A LocationListener to initialize the network location provider. The location updates
5432416e09649eb6ab767eba458796e126196c77a34Jeff Brown    // are handled through the passive location provider.
5442416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final LocationListener mEmptyLocationListener =  new LocationListener() {
5452416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onLocationChanged(Location location) {
5462416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5472416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5482416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onProviderDisabled(String provider) {
5492416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5502416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5512416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onProviderEnabled(String provider) {
5522416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5532416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5542416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onStatusChanged(String provider, int status, Bundle extras) {
5552416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5562416e09649eb6ab767eba458796e126196c77a34Jeff Brown    };
5572416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5582416e09649eb6ab767eba458796e126196c77a34Jeff Brown    private final LocationListener mLocationListener = new LocationListener() {
5592416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onLocationChanged(Location location) {
5602416e09649eb6ab767eba458796e126196c77a34Jeff Brown            mLocationHandler.processNewLocation(location);
5612416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5622416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5632416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onProviderDisabled(String provider) {
5642416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5652416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5662416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onProviderEnabled(String provider) {
5672416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5682416e09649eb6ab767eba458796e126196c77a34Jeff Brown
5692416e09649eb6ab767eba458796e126196c77a34Jeff Brown        public void onStatusChanged(String provider, int status, Bundle extras) {
5702416e09649eb6ab767eba458796e126196c77a34Jeff Brown        }
5712416e09649eb6ab767eba458796e126196c77a34Jeff Brown    };
5722416e09649eb6ab767eba458796e126196c77a34Jeff Brown}
573