TwilightManager.java revision f0ec407dc5fdb420550ed29f9556909bd9f635a9
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v7.app; 18 19import android.Manifest; 20import android.content.Context; 21import android.location.Location; 22import android.location.LocationManager; 23import android.support.annotation.NonNull; 24import android.support.v4.content.PermissionChecker; 25import android.text.format.DateUtils; 26import android.util.Log; 27 28import java.util.Calendar; 29 30/** 31 * Class which managing whether we are in the night or not. 32 */ 33class TwilightManager { 34 35 private static final String TAG = "TwilightManager"; 36 37 private static final int SUNRISE = 6; // 6am 38 private static final int SUNSET = 22; // 10pm 39 40 private static final TwilightState sTwilightState = new TwilightState(); 41 42 private final Context mContext; 43 private final LocationManager mLocationManager; 44 45 TwilightManager(Context context) { 46 mContext = context; 47 mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 48 } 49 50 /** 51 * Returns true we are currently in the 'night'. 52 * 53 * @return true if we are at night, false if the day. 54 */ 55 boolean isNight() { 56 final TwilightState state = sTwilightState; 57 58 if (isStateValid(state)) { 59 // If the current twilight state is still valid, use it 60 return state.isNight; 61 } 62 63 // Else, we will try and grab the last known location 64 final Location location = getLastKnownLocation(); 65 if (location != null) { 66 updateState(location); 67 return state.isNight; 68 } 69 70 Log.i(TAG, "Could not get last known location. This is probably because the app does not" 71 + " have any location permissions. Falling back to hardcoded" 72 + " sunrise/sunset values."); 73 74 // If we don't have a location, we'll use our hardcoded sunrise/sunset values. 75 // These aren't great, but it's better than nothing. 76 Calendar calendar = Calendar.getInstance(); 77 final int hour = calendar.get(Calendar.HOUR_OF_DAY); 78 return hour < SUNRISE || hour >= SUNSET; 79 } 80 81 private Location getLastKnownLocation() { 82 Location coarseLocation = null; 83 Location fineLocation = null; 84 85 int permission = PermissionChecker.checkSelfPermission(mContext, 86 Manifest.permission.ACCESS_FINE_LOCATION); 87 if (permission == PermissionChecker.PERMISSION_GRANTED) { 88 coarseLocation = getLastKnownLocationForProvider(LocationManager.NETWORK_PROVIDER); 89 } 90 91 permission = PermissionChecker.checkSelfPermission(mContext, 92 Manifest.permission.ACCESS_COARSE_LOCATION); 93 if (permission == PermissionChecker.PERMISSION_GRANTED) { 94 fineLocation = getLastKnownLocationForProvider(LocationManager.GPS_PROVIDER); 95 } 96 97 if (coarseLocation != null && fineLocation != null) { 98 // If we have both a fine and coarse location, use the latest 99 if (fineLocation.getTime() > coarseLocation.getTime()) { 100 return fineLocation; 101 } else { 102 return coarseLocation; 103 } 104 } else { 105 // Else, return the non-null one (if there is one) 106 return fineLocation != null ? fineLocation : coarseLocation; 107 } 108 } 109 110 private Location getLastKnownLocationForProvider(String provider) { 111 if (mLocationManager != null) { 112 try { 113 if (mLocationManager.isProviderEnabled(provider)) { 114 return mLocationManager.getLastKnownLocation(provider); 115 } 116 } catch (Exception e) { 117 Log.d(TAG, "Failed to get last known location", e); 118 } 119 } 120 return null; 121 } 122 123 private boolean isStateValid(TwilightState state) { 124 return state != null && state.nextUpdate > System.currentTimeMillis(); 125 } 126 127 private void updateState(@NonNull Location location) { 128 final TwilightState state = sTwilightState; 129 final long now = System.currentTimeMillis(); 130 final TwilightCalculator calculator = TwilightCalculator.getInstance(); 131 132 // calculate yesterday's twilight 133 calculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS, 134 location.getLatitude(), location.getLongitude()); 135 final long yesterdaySunset = calculator.sunset; 136 137 // calculate today's twilight 138 calculator.calculateTwilight(now, location.getLatitude(), location.getLongitude()); 139 final boolean isNight = (calculator.state == TwilightCalculator.NIGHT); 140 final long todaySunrise = calculator.sunrise; 141 final long todaySunset = calculator.sunset; 142 143 // calculate tomorrow's twilight 144 calculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS, 145 location.getLatitude(), location.getLongitude()); 146 final long tomorrowSunrise = calculator.sunrise; 147 148 // Set next update 149 long nextUpdate = 0; 150 if (todaySunrise == -1 || todaySunset == -1) { 151 // In the case the day or night never ends the update is scheduled 12 hours later. 152 nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS; 153 } else { 154 if (now > todaySunset) { 155 nextUpdate += tomorrowSunrise; 156 } else if (now > todaySunrise) { 157 nextUpdate += todaySunset; 158 } else { 159 nextUpdate += todaySunrise; 160 } 161 // add some extra time to be on the safe side. 162 nextUpdate += DateUtils.MINUTE_IN_MILLIS; 163 } 164 165 // Update the twilight state 166 state.isNight = isNight; 167 state.yesterdaySunset = yesterdaySunset; 168 state.todaySunrise = todaySunrise; 169 state.todaySunset = todaySunset; 170 state.tomorrowSunrise = tomorrowSunrise; 171 state.nextUpdate = nextUpdate; 172 } 173 174 /** 175 * Describes whether it is day or night. 176 */ 177 private static class TwilightState { 178 boolean isNight; 179 long yesterdaySunset; 180 long todaySunrise; 181 long todaySunset; 182 long tomorrowSunrise; 183 long nextUpdate; 184 } 185} 186