Custom dark theme scheduling
allows the use to set the start and end automatic dark theme activation within a day. Fixes: 147649309 Test: atest UiModeManagerServiceTest UiModeManagerTest Change-Id: Iaa3593d4e8863412e3703ce9f089b88dd4df1225
This commit is contained in:
@@ -6470,7 +6470,11 @@ package android.app {
|
||||
method public void disableCarMode(int);
|
||||
method public void enableCarMode(int);
|
||||
method public int getCurrentModeType();
|
||||
method @NonNull public java.time.LocalTime getCustomNightModeEnd();
|
||||
method @NonNull public java.time.LocalTime getCustomNightModeStart();
|
||||
method public int getNightMode();
|
||||
method public void setCustomNightModeEnd(@NonNull java.time.LocalTime);
|
||||
method public void setCustomNightModeStart(@NonNull java.time.LocalTime);
|
||||
method public void setNightMode(int);
|
||||
field public static String ACTION_ENTER_CAR_MODE;
|
||||
field public static String ACTION_ENTER_DESK_MODE;
|
||||
@@ -6480,6 +6484,7 @@ package android.app {
|
||||
field public static final int ENABLE_CAR_MODE_ALLOW_SLEEP = 2; // 0x2
|
||||
field public static final int ENABLE_CAR_MODE_GO_CAR_HOME = 1; // 0x1
|
||||
field public static final int MODE_NIGHT_AUTO = 0; // 0x0
|
||||
field public static final int MODE_NIGHT_CUSTOM = 3; // 0x3
|
||||
field public static final int MODE_NIGHT_NO = 1; // 0x1
|
||||
field public static final int MODE_NIGHT_YES = 2; // 0x2
|
||||
}
|
||||
@@ -50559,6 +50564,7 @@ package android.util {
|
||||
method public static java.util.TimeZone getTimeZone(int, boolean, long, String);
|
||||
method public static String getTimeZoneDatabaseVersion();
|
||||
method @Nullable public static java.util.List<java.lang.String> getTimeZoneIdsForCountryCode(@NonNull String);
|
||||
method public static boolean isTimeBetween(@NonNull java.time.LocalTime, @NonNull java.time.LocalTime, @NonNull java.time.LocalTime);
|
||||
}
|
||||
|
||||
@Deprecated public class TimingLogger {
|
||||
|
||||
@@ -1,33 +1,48 @@
|
||||
// Baseline format: 1.0
|
||||
AcronymName: android.net.NetworkCapabilities#setSSID(String):
|
||||
Acronyms should not be capitalized in method names: was `setSSID`, should this be `setSsid`?
|
||||
|
||||
|
||||
ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
|
||||
|
||||
ActionValue: android.net.wifi.WifiManager#ACTION_LINK_CONFIGURATION_CHANGED:
|
||||
Inconsistent action value; expected `android.net.wifi.action.LINK_CONFIGURATION_CHANGED`, was `android.net.wifi.LINK_CONFIGURATION_CHANGED`
|
||||
|
||||
|
||||
|
||||
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #1:
|
||||
Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
|
||||
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #2:
|
||||
Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
|
||||
ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsLocalCapabilities():
|
||||
Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
|
||||
ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsSelectableCapabilities():
|
||||
Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
|
||||
ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #0:
|
||||
Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
|
||||
ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #1:
|
||||
Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
|
||||
ArrayReturn: android.media.tv.tuner.Tuner.FilterCallback#onFilterEvent(android.media.tv.tuner.Tuner.Filter, android.media.tv.tuner.filter.FilterEvent[]) parameter #1:
|
||||
Method parameter should be Collection<FilterEvent> (or subclass) instead of raw array; was `android.media.tv.tuner.filter.FilterEvent[]`
|
||||
ArrayReturn: android.net.NetworkScoreManager#requestScores(android.net.NetworkKey[]) parameter #0:
|
||||
Method parameter should be Collection<NetworkKey> (or subclass) instead of raw array; was `android.net.NetworkKey[]`
|
||||
ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
|
||||
|
||||
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#deletePersistentGroup(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `deletePersistentGroup`
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `factoryReset`
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#listen(android.net.wifi.p2p.WifiP2pManager.Channel, boolean, android.net.wifi.p2p.WifiP2pManager.ActionListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `listen`
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#requestPersistentGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `requestPersistentGroupInfo`
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setDeviceName(android.net.wifi.p2p.WifiP2pManager.Channel, String, android.net.wifi.p2p.WifiP2pManager.ActionListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `setDeviceName`
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setWfdInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pWfdInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `setWfdInfo`
|
||||
|
||||
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setWifiP2pChannels(android.net.wifi.p2p.WifiP2pManager.Channel, int, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
|
||||
Registration methods should have overload that accepts delivery Executor: `setWifiP2pChannels`
|
||||
|
||||
HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities():
|
||||
Type must not be heavy BitSet (method android.net.wifi.wificond.NativeScanResult.getCapabilities())
|
||||
PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
|
||||
Found registerApCallback but not unregisterApCallback in android.net.wifi.wificond.WifiCondManager
|
||||
|
||||
|
||||
|
||||
GenericException: android.app.prediction.AppPredictor#finalize():
|
||||
@@ -40,13 +55,22 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
|
||||
|
||||
|
||||
|
||||
HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities():
|
||||
|
||||
|
||||
|
||||
IntentBuilderName: android.content.Context#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
|
||||
Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers`
|
||||
|
||||
|
||||
KotlinKeyword: android.app.Notification#when:
|
||||
|
||||
|
||||
|
||||
KotlinOperator: android.telephony.CbGeoUtils.Geometry#contains(android.telephony.CbGeoUtils.LatLng):
|
||||
Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
|
||||
|
||||
|
||||
MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
|
||||
|
||||
MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#writeToParcel(android.os.Parcel, int) parameter #0:
|
||||
@@ -70,7 +94,7 @@ MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent
|
||||
MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
|
||||
|
||||
MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
|
||||
|
||||
|
||||
MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
|
||||
|
||||
MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
|
||||
@@ -157,43 +181,60 @@ MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(
|
||||
|
||||
|
||||
|
||||
|
||||
MutableBareField: android.net.IpConfiguration#httpProxy:
|
||||
Bare field httpProxy must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.IpConfiguration#ipAssignment:
|
||||
Bare field ipAssignment must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.IpConfiguration#proxySettings:
|
||||
Bare field proxySettings must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.IpConfiguration#staticIpConfiguration:
|
||||
Bare field staticIpConfiguration must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#allowAutojoin:
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#apBand:
|
||||
Bare field apBand must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#carrierId:
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSpecifier:
|
||||
Bare field fromWifiNetworkSpecifier must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSuggestion:
|
||||
Bare field fromWifiNetworkSuggestion must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#macRandomizationSetting:
|
||||
Bare field macRandomizationSetting must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#meteredOverride:
|
||||
Bare field meteredOverride must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#requirePMF:
|
||||
Bare field requirePMF must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#saePasswordId:
|
||||
Bare field saePasswordId must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiConfiguration#shared:
|
||||
Bare field shared must be marked final, or moved behind accessors if mutable
|
||||
|
||||
MutableBareField: android.net.wifi.WifiScanner.ScanSettings#type:
|
||||
Bare field type must be marked final, or moved behind accessors if mutable
|
||||
|
||||
|
||||
|
||||
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
|
||||
|
||||
|
||||
|
||||
NotCloseable: android.bluetooth.BluetoothA2dpSink:
|
||||
Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothA2dpSink
|
||||
NotCloseable: android.bluetooth.BluetoothMap:
|
||||
Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothMap
|
||||
NotCloseable: android.bluetooth.BluetoothPan:
|
||||
Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPan
|
||||
NotCloseable: android.bluetooth.BluetoothPbap:
|
||||
Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPbap
|
||||
|
||||
|
||||
OnNameExpected: android.content.ContentProvider#checkUriPermission(android.net.Uri, int, int):
|
||||
If implemented by developer, should follow the on<Something> style; otherwise consider marking final
|
||||
|
||||
|
||||
PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
|
||||
|
||||
|
||||
|
||||
ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
|
||||
|
||||
ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
|
||||
@@ -201,6 +242,7 @@ ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.
|
||||
ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
|
||||
|
||||
|
||||
|
||||
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
|
||||
|
||||
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
|
||||
@@ -246,9 +288,11 @@ SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.ap
|
||||
SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
|
||||
|
||||
SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String):
|
||||
|
||||
|
||||
SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
|
||||
SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
|
||||
SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback):
|
||||
|
||||
|
||||
SamShouldBeLast: android.content.Context#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection):
|
||||
|
||||
SamShouldBeLast: android.content.Context#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection):
|
||||
@@ -279,12 +323,20 @@ SamShouldBeLast: android.location.LocationManager#registerGnssNavigationMessageC
|
||||
|
||||
SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
|
||||
|
||||
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper):
|
||||
SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
|
||||
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
|
||||
|
||||
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
|
||||
|
||||
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper):
|
||||
SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
|
||||
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
|
||||
|
||||
SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper):
|
||||
SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
|
||||
SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper):
|
||||
SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
|
||||
SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler):
|
||||
|
||||
SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
|
||||
@@ -380,7 +432,7 @@ SamShouldBeLast: android.telephony.TelephonyManager#requestNetworkScan(android.t
|
||||
SamShouldBeLast: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
|
||||
|
||||
SamShouldBeLast: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
|
||||
|
||||
|
||||
SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
|
||||
|
||||
SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
|
||||
@@ -445,3 +497,11 @@ ServiceName: android.Manifest.permission#REQUEST_NOTIFICATION_ASSISTANT_SERVICE:
|
||||
|
||||
ServiceName: android.provider.DeviceConfig#NAMESPACE_PACKAGE_MANAGER_SERVICE:
|
||||
|
||||
|
||||
|
||||
UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
|
||||
When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
|
||||
|
||||
|
||||
UserHandleName: android.telephony.CellBroadcastIntents#sendOrderedBroadcastForBackgroundReceivers(android.content.Context, android.os.UserHandle, android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle):
|
||||
Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `sendOrderedBroadcastForBackgroundReceivers`
|
||||
|
||||
@@ -70,7 +70,27 @@ interface IUiModeManager {
|
||||
boolean isNightModeLocked();
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* [De]Activates night mode
|
||||
*/
|
||||
boolean setNightModeActivated(boolean active);
|
||||
|
||||
/**
|
||||
* Returns custom start clock time
|
||||
*/
|
||||
long getCustomNightModeStart();
|
||||
|
||||
/**
|
||||
* Sets custom start clock time
|
||||
*/
|
||||
void setCustomNightModeStart(long time);
|
||||
|
||||
/**
|
||||
* Returns custom end clock time
|
||||
*/
|
||||
long getCustomNightModeEnd();
|
||||
|
||||
/**
|
||||
* Sets custom end clock time
|
||||
*/
|
||||
void setCustomNightModeEnd(long time);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.app;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
@@ -32,6 +33,7 @@ import android.os.ServiceManager.ServiceNotFoundException;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* This class provides access to the system uimode services. These services
|
||||
@@ -163,6 +165,7 @@ public class UiModeManager {
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "MODE_" }, value = {
|
||||
MODE_NIGHT_AUTO,
|
||||
MODE_NIGHT_CUSTOM,
|
||||
MODE_NIGHT_NO,
|
||||
MODE_NIGHT_YES
|
||||
})
|
||||
@@ -173,19 +176,25 @@ public class UiModeManager {
|
||||
* Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
|
||||
* automatically switch night mode on and off based on the time.
|
||||
*/
|
||||
public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
|
||||
public static final int MODE_NIGHT_AUTO = 0;
|
||||
|
||||
/**
|
||||
* Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
|
||||
* automatically switch night mode on and off based on the time.
|
||||
*/
|
||||
public static final int MODE_NIGHT_CUSTOM = 3;
|
||||
|
||||
/**
|
||||
* Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
|
||||
* never run in night mode.
|
||||
*/
|
||||
public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
|
||||
public static final int MODE_NIGHT_NO = 1;
|
||||
|
||||
/**
|
||||
* Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
|
||||
* always run in night mode.
|
||||
*/
|
||||
public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
|
||||
public static final int MODE_NIGHT_YES = 2;
|
||||
|
||||
private IUiModeManager mService;
|
||||
|
||||
@@ -377,6 +386,8 @@ public class UiModeManager {
|
||||
* {@code notnight} mode</li>
|
||||
* <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
|
||||
* {@code night} mode</li>
|
||||
* <li><em>{@link #MODE_NIGHT_CUSTOM}</em> automatically switches between
|
||||
* {@code night} and {@code notnight} based on the custom time set (or default)</li>
|
||||
* <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
|
||||
* {@code night} and {@code notnight} based on the device's current
|
||||
* location and certain other sensors</li>
|
||||
@@ -418,6 +429,7 @@ public class UiModeManager {
|
||||
* <li>{@link #MODE_NIGHT_NO}</li>
|
||||
* <li>{@link #MODE_NIGHT_YES}</li>
|
||||
* <li>{@link #MODE_NIGHT_AUTO}</li>
|
||||
* <li>{@link #MODE_NIGHT_CUSTOM}</li>
|
||||
* <li>{@code -1} on error</li>
|
||||
* </ul>
|
||||
*
|
||||
@@ -475,7 +487,7 @@ public class UiModeManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide*
|
||||
* @hide
|
||||
*/
|
||||
public boolean setNightModeActivated(boolean active) {
|
||||
if (mService != null) {
|
||||
@@ -487,4 +499,75 @@ public class UiModeManager {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of the day Dark theme activates
|
||||
* <p>
|
||||
* When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
|
||||
* this time set to activate it automatically.
|
||||
*/
|
||||
@NonNull
|
||||
public LocalTime getCustomNightModeStart() {
|
||||
if (mService != null) {
|
||||
try {
|
||||
return LocalTime.ofNanoOfDay(mService.getCustomNightModeStart() * 1000);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
return LocalTime.MIDNIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time of the day Dark theme activates
|
||||
* <p>
|
||||
* When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
|
||||
* this time set to activate it automatically
|
||||
* @param time The time of the day Dark theme should activate
|
||||
*/
|
||||
public void setCustomNightModeStart(@NonNull LocalTime time) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.setCustomNightModeStart(time.toNanoOfDay() / 1000);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time of the day Dark theme deactivates
|
||||
* <p>
|
||||
* When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
|
||||
* this time set to deactivate it automatically.
|
||||
*/
|
||||
@NonNull
|
||||
public LocalTime getCustomNightModeEnd() {
|
||||
if (mService != null) {
|
||||
try {
|
||||
return LocalTime.ofNanoOfDay(mService.getCustomNightModeEnd() * 1000);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
return LocalTime.MIDNIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time of the day Dark theme deactivates
|
||||
* <p>
|
||||
* When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
|
||||
* this time set to deactivate it automatically.
|
||||
* @param time The time of the day Dark theme should deactivate
|
||||
*/
|
||||
public void setCustomNightModeEnd(@NonNull LocalTime time) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5882,6 +5882,22 @@ public final class Settings {
|
||||
public static final String DARK_MODE_DIALOG_SEEN =
|
||||
"dark_mode_dialog_seen";
|
||||
|
||||
/**
|
||||
* Custom time when Dark theme is scheduled to activate.
|
||||
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
|
||||
* @hide
|
||||
*/
|
||||
public static final String DARK_THEME_CUSTOM_START_TIME =
|
||||
"dark_theme_custom_start_time";
|
||||
|
||||
/**
|
||||
* Custom time when Dark theme is scheduled to deactivate.
|
||||
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
|
||||
* @hide
|
||||
*/
|
||||
public static final String DARK_THEME_CUSTOM_END_TIME =
|
||||
"dark_theme_custom_end_time";
|
||||
|
||||
/**
|
||||
* Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
|
||||
*
|
||||
@@ -7705,6 +7721,14 @@ public final class Settings {
|
||||
*/
|
||||
public static final String UI_NIGHT_MODE = "ui_night_mode";
|
||||
|
||||
/**
|
||||
* The current night mode that has been overrided by the system. Owned
|
||||
* and controlled by UiModeManagerService. Constants are as per
|
||||
* UiModeManager.
|
||||
* @hide
|
||||
*/
|
||||
public static final String UI_NIGHT_MODE_OVERRIDE = "ui_night_mode_override";
|
||||
|
||||
/**
|
||||
* Whether screensavers are enabled.
|
||||
* @hide
|
||||
|
||||
@@ -30,6 +30,7 @@ import libcore.timezone.ZoneInfoDB;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
@@ -381,6 +382,28 @@ public class TimeUtils {
|
||||
pw.print(sDumpDateFormat.format(new Date(time)));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to find if a clock time is inclusively between two other clock times
|
||||
* @param reference The time of the day we want check if it is between start and end
|
||||
* @param start The start time reference
|
||||
* @param end The end time
|
||||
* @return true if the reference time is between the two clock times, and false otherwise.
|
||||
*/
|
||||
public static boolean isTimeBetween(@NonNull LocalTime reference,
|
||||
@NonNull LocalTime start,
|
||||
@NonNull LocalTime end) {
|
||||
// ////////E----+-----S////////
|
||||
if ((reference.isBefore(start) && reference.isAfter(end)
|
||||
// -----+----S//////////E------
|
||||
|| (reference.isBefore(end) && reference.isBefore(start) && start.isBefore(end))
|
||||
// ---------S//////////E---+---
|
||||
|| (reference.isAfter(end) && reference.isAfter(start)) && start.isBefore(end))) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump a currentTimeMillis style timestamp for dumpsys, with the delta time from now.
|
||||
*
|
||||
|
||||
@@ -2562,4 +2562,10 @@ enum PageId {
|
||||
// CATEGORY: SETTINGS
|
||||
// OS: R
|
||||
OPEN_SUPPORTED_LINKS = 1824;
|
||||
|
||||
// OPEN: Settings > Display > Dark theme > Set start time dialog
|
||||
DIALOG_DARK_THEME_SET_START_TIME = 1825;
|
||||
|
||||
// OPEN: Settings > Display > Dark theme > Set end time dialog
|
||||
DIALOG_DARK_THEME_SET_END_TIME = 1826;
|
||||
}
|
||||
|
||||
@@ -920,6 +920,10 @@
|
||||
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset">On at sunset</string>
|
||||
<!-- QuickSettings: Secondary text for when the Dark Mode will be on until sunrise. [CHAR LIMIT=20] -->
|
||||
<string name="quick_settings_dark_mode_secondary_label_until_sunrise">Until sunrise</string>
|
||||
<!-- QuickSettings: Secondary text for when the Dark theme will be enabled at some user-selected time. [CHAR LIMIT=20] -->
|
||||
<string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
|
||||
<!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
|
||||
<string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
|
||||
|
||||
<!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
|
||||
<string name="quick_settings_nfc_label">NFC</string>
|
||||
|
||||
@@ -33,6 +33,8 @@ import com.android.systemui.statusbar.policy.BatteryController;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Quick Settings tile for: Night Mode / Dark Theme / Dark Mode.
|
||||
@@ -43,7 +45,7 @@ import javax.inject.Inject;
|
||||
public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
|
||||
ConfigurationController.ConfigurationListener,
|
||||
BatteryController.BatteryStateChangeCallback {
|
||||
|
||||
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
|
||||
private final Icon mIcon = ResourceIcon.get(
|
||||
com.android.internal.R.drawable.ic_qs_ui_mode_night);
|
||||
private final UiModeManager mUiModeManager;
|
||||
@@ -88,17 +90,28 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
|
||||
protected void handleUpdateState(BooleanState state, Object arg) {
|
||||
int uiMode = mUiModeManager.getNightMode();
|
||||
boolean powerSave = mBatteryController.isPowerSave();
|
||||
boolean isAuto = uiMode == UiModeManager.MODE_NIGHT_AUTO;
|
||||
boolean nightMode = (mContext.getResources().getConfiguration().uiMode
|
||||
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
||||
|
||||
if (powerSave) {
|
||||
state.secondaryLabel = mContext.getResources().getString(
|
||||
R.string.quick_settings_dark_mode_secondary_label_battery_saver);
|
||||
} else if (isAuto) {
|
||||
} else if (uiMode == UiModeManager.MODE_NIGHT_AUTO) {
|
||||
state.secondaryLabel = mContext.getResources().getString(nightMode
|
||||
? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
|
||||
: R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
|
||||
} else if (uiMode == UiModeManager.MODE_NIGHT_CUSTOM) {
|
||||
final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat(mContext);
|
||||
final LocalTime time;
|
||||
if (nightMode) {
|
||||
time = mUiModeManager.getCustomNightModeEnd();
|
||||
} else {
|
||||
time = mUiModeManager.getCustomNightModeStart();
|
||||
}
|
||||
state.secondaryLabel = mContext.getResources().getString(nightMode
|
||||
? R.string.quick_settings_dark_mode_secondary_label_until
|
||||
: R.string.quick_settings_dark_mode_secondary_label_on_at,
|
||||
use24HourFormat ? time.toString() : formatter.format(time));
|
||||
} else {
|
||||
state.secondaryLabel = null;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.IUiModeManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
@@ -70,10 +71,19 @@ import com.android.server.wm.WindowManagerInternal;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
|
||||
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
|
||||
import static android.app.UiModeManager.MODE_NIGHT_YES;
|
||||
import static android.util.TimeUtils.isTimeBetween;
|
||||
|
||||
final class UiModeManagerService extends SystemService {
|
||||
private static final String TAG = UiModeManager.class.getSimpleName();
|
||||
private static final boolean LOG = false;
|
||||
@@ -90,7 +100,12 @@ final class UiModeManagerService extends SystemService {
|
||||
// we use the override auto mode
|
||||
// for example: force night mode off in the night time while in auto mode
|
||||
private int mNightModeOverride = mNightMode;
|
||||
protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE + "_override";
|
||||
private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
|
||||
private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
|
||||
private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
|
||||
private LocalTime mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME;
|
||||
|
||||
protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE_OVERRIDE;
|
||||
|
||||
private Map<Integer, String> mCarModePackagePriority = new HashMap<>();
|
||||
private boolean mCarModeEnabled = false;
|
||||
@@ -131,6 +146,8 @@ final class UiModeManagerService extends SystemService {
|
||||
private NotificationManager mNotificationManager;
|
||||
private StatusBarManager mStatusBarManager;
|
||||
private WindowManagerInternal mWindowManager;
|
||||
private AlarmManager mAlarmManager;
|
||||
private PowerManager mPowerManager;
|
||||
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
@@ -141,14 +158,16 @@ final class UiModeManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected UiModeManagerService(Context context, WindowManagerInternal wm,
|
||||
PowerManager.WakeLock wl, TwilightManager tm,
|
||||
protected UiModeManagerService(Context context, WindowManagerInternal wm, AlarmManager am,
|
||||
PowerManager pm, PowerManager.WakeLock wl, TwilightManager tm,
|
||||
boolean setupWizardComplete) {
|
||||
super(context);
|
||||
mWindowManager = wm;
|
||||
mWakeLock = wl;
|
||||
mTwilightManager = tm;
|
||||
mSetupWizardComplete = setupWizardComplete;
|
||||
mAlarmManager = am;
|
||||
mPowerManager = pm;
|
||||
}
|
||||
|
||||
private static Intent buildHomeIntent(String category) {
|
||||
@@ -237,6 +256,21 @@ final class UiModeManagerService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mOnTimeChangedHandler = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
synchronized (mLock) {
|
||||
updateCustomTimeLocked();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final AlarmManager.OnAlarmListener mCustomTimeListener = () -> {
|
||||
synchronized (mLock) {
|
||||
updateCustomTimeLocked();
|
||||
}
|
||||
};
|
||||
|
||||
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
|
||||
@Override
|
||||
public void onVrStateChanged(boolean enabled) {
|
||||
@@ -270,8 +304,9 @@ final class UiModeManagerService extends SystemService {
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
|
||||
mNightMode, 0);
|
||||
mode = mode == UiModeManager.MODE_NIGHT_AUTO
|
||||
? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
|
||||
if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
|
||||
mode = MODE_NIGHT_YES;
|
||||
}
|
||||
SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
|
||||
}
|
||||
};
|
||||
@@ -287,10 +322,11 @@ final class UiModeManagerService extends SystemService {
|
||||
public void onStart() {
|
||||
final Context context = getContext();
|
||||
|
||||
final PowerManager powerManager =
|
||||
mPowerManager =
|
||||
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
|
||||
mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
|
||||
mWindowManager = LocalServices.getService(WindowManagerInternal.class);
|
||||
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
// If setup isn't complete for this user listen for completion so we can unblock
|
||||
// being able to send a night mode configuration change event
|
||||
@@ -387,6 +423,16 @@ final class UiModeManagerService extends SystemService {
|
||||
Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
|
||||
}
|
||||
|
||||
private void updateCustomTimeLocked() {
|
||||
if (mNightMode != MODE_NIGHT_CUSTOM) return;
|
||||
if (shouldApplyAutomaticChangesImmediately()) {
|
||||
updateLocked(0, 0);
|
||||
} else {
|
||||
registerScreenOffEvent();
|
||||
}
|
||||
scheduleNextCustomTimeListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the night mode setting in Settings.Global and returns if the value was successfully
|
||||
* changed.
|
||||
@@ -404,9 +450,19 @@ final class UiModeManagerService extends SystemService {
|
||||
Secure.UI_NIGHT_MODE, defaultNightMode, userId);
|
||||
mNightModeOverride = Secure.getIntForUser(context.getContentResolver(),
|
||||
OVERRIDE_NIGHT_MODE, defaultNightMode, userId);
|
||||
mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay(
|
||||
Secure.getLongForUser(context.getContentResolver(),
|
||||
Secure.DARK_THEME_CUSTOM_START_TIME,
|
||||
DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000);
|
||||
mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay(
|
||||
Secure.getLongForUser(context.getContentResolver(),
|
||||
Secure.DARK_THEME_CUSTOM_END_TIME,
|
||||
DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
|
||||
} else {
|
||||
mNightMode = defaultNightMode;
|
||||
mNightModeOverride = defaultNightMode;
|
||||
mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME;
|
||||
mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
|
||||
}
|
||||
|
||||
return oldNightMode != mNightMode;
|
||||
@@ -419,6 +475,10 @@ final class UiModeManagerService extends SystemService {
|
||||
getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
|
||||
}
|
||||
|
||||
private void cancelCustomAlarm() {
|
||||
mAlarmManager.cancel(mCustomTimeListener);
|
||||
}
|
||||
|
||||
private void unregisterScreenOffEvent() {
|
||||
mWaitForScreenOff = false;
|
||||
try {
|
||||
@@ -428,6 +488,21 @@ final class UiModeManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTimeChangeEvent() {
|
||||
final IntentFilter intentFilter =
|
||||
new IntentFilter(Intent.ACTION_TIME_CHANGED);
|
||||
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
|
||||
getContext().registerReceiver(mOnTimeChangedHandler, intentFilter);
|
||||
}
|
||||
|
||||
private void unregisterTimeChangeEvent() {
|
||||
try {
|
||||
getContext().unregisterReceiver(mOnTimeChangedHandler);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// we ignore this exception if the receiver is unregistered already.
|
||||
}
|
||||
}
|
||||
|
||||
private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
|
||||
@Override
|
||||
public void enableCarMode(@UiModeManager.EnableCarMode int flags,
|
||||
@@ -537,7 +612,8 @@ final class UiModeManagerService extends SystemService {
|
||||
switch (mode) {
|
||||
case UiModeManager.MODE_NIGHT_NO:
|
||||
case UiModeManager.MODE_NIGHT_YES:
|
||||
case UiModeManager.MODE_NIGHT_AUTO:
|
||||
case MODE_NIGHT_AUTO:
|
||||
case MODE_NIGHT_CUSTOM:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown mode: " + mode);
|
||||
@@ -548,8 +624,9 @@ final class UiModeManagerService extends SystemService {
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
if (mNightMode != mode) {
|
||||
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
|
||||
if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
|
||||
unregisterScreenOffEvent();
|
||||
cancelCustomAlarm();
|
||||
}
|
||||
|
||||
mNightMode = mode;
|
||||
@@ -559,7 +636,9 @@ final class UiModeManagerService extends SystemService {
|
||||
persistNightMode(user);
|
||||
}
|
||||
// on screen off will update configuration instead
|
||||
if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) {
|
||||
if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
|
||||
|| shouldApplyAutomaticChangesImmediately()) {
|
||||
unregisterScreenOffEvent();
|
||||
updateLocked(0, 0);
|
||||
} else {
|
||||
registerScreenOffEvent();
|
||||
@@ -610,7 +689,7 @@ final class UiModeManagerService extends SystemService {
|
||||
final int user = UserHandle.getCallingUserId();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
|
||||
if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
|
||||
unregisterScreenOffEvent();
|
||||
mNightModeOverride = active
|
||||
? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
|
||||
@@ -630,8 +709,74 @@ final class UiModeManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCustomNightModeStart() {
|
||||
return mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomNightModeStart(long time) {
|
||||
if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
|
||||
return;
|
||||
}
|
||||
final int user = UserHandle.getCallingUserId();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
|
||||
if (newTime == null) return;
|
||||
mCustomAutoNightModeStartMilliseconds = newTime;
|
||||
persistNightMode(user);
|
||||
onCustomTimeUpdated(user);
|
||||
} catch (DateTimeException e) {
|
||||
unregisterScreenOffEvent();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCustomNightModeEnd() {
|
||||
return mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomNightModeEnd(long time) {
|
||||
if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
|
||||
return;
|
||||
}
|
||||
final int user = UserHandle.getCallingUserId();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
|
||||
if (newTime == null) return;
|
||||
mCustomAutoNightModeEndMilliseconds = newTime;
|
||||
onCustomTimeUpdated(user);
|
||||
} catch (DateTimeException e) {
|
||||
unregisterScreenOffEvent();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void onCustomTimeUpdated(int user) {
|
||||
persistNightMode(user);
|
||||
if (mNightMode != MODE_NIGHT_CUSTOM) return;
|
||||
if (shouldApplyAutomaticChangesImmediately()) {
|
||||
unregisterScreenOffEvent();
|
||||
updateLocked(0, 0);
|
||||
} else {
|
||||
registerScreenOffEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void dumpImpl(PrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
pw.println("Current UI Mode Service state:");
|
||||
@@ -677,7 +822,6 @@ final class UiModeManagerService extends SystemService {
|
||||
mTwilightManager = getLocalService(TwilightManager.class);
|
||||
mSystemReady = true;
|
||||
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
|
||||
updateComputedNightModeLocked();
|
||||
registerVrStateListener();
|
||||
updateLocked(0, 0);
|
||||
}
|
||||
@@ -838,6 +982,12 @@ final class UiModeManagerService extends SystemService {
|
||||
Secure.UI_NIGHT_MODE, mNightMode, user);
|
||||
Secure.putIntForUser(getContext().getContentResolver(),
|
||||
OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
|
||||
Secure.putLongForUser(getContext().getContentResolver(),
|
||||
Secure.DARK_THEME_CUSTOM_START_TIME,
|
||||
mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user);
|
||||
Secure.putLongForUser(getContext().getContentResolver(),
|
||||
Secure.DARK_THEME_CUSTOM_END_TIME,
|
||||
mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user);
|
||||
}
|
||||
|
||||
private void updateConfigurationLocked() {
|
||||
@@ -856,13 +1006,16 @@ final class UiModeManagerService extends SystemService {
|
||||
uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
|
||||
}
|
||||
|
||||
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
|
||||
if (mNightMode == MODE_NIGHT_AUTO) {
|
||||
boolean activateNightMode = mComputedNightMode;
|
||||
if (mTwilightManager != null) {
|
||||
mTwilightManager.registerListener(mTwilightListener, mHandler);
|
||||
final TwilightState lastState = mTwilightManager.getLastTwilightState();
|
||||
activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight();
|
||||
}
|
||||
updateComputedNightModeLocked();
|
||||
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
|
||||
: Configuration.UI_MODE_NIGHT_NO;
|
||||
|
||||
updateComputedNightModeLocked(activateNightMode);
|
||||
uiMode = getComputedUiModeConfiguration(uiMode);
|
||||
} else {
|
||||
if (mTwilightManager != null) {
|
||||
mTwilightManager.unregisterListener(mTwilightListener);
|
||||
@@ -870,6 +1023,16 @@ final class UiModeManagerService extends SystemService {
|
||||
uiMode |= mNightMode << 4;
|
||||
}
|
||||
|
||||
if (mNightMode == MODE_NIGHT_CUSTOM) {
|
||||
registerTimeChangeEvent();
|
||||
final boolean activate = computeCustomNightMode();
|
||||
updateComputedNightModeLocked(activate);
|
||||
scheduleNextCustomTimeListener();
|
||||
uiMode = getComputedUiModeConfiguration(uiMode);
|
||||
} else {
|
||||
unregisterTimeChangeEvent();
|
||||
}
|
||||
|
||||
// Override night mode in power save mode if not in car mode
|
||||
if (mPowerSave && !mCarModeEnabled) {
|
||||
uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
|
||||
@@ -885,11 +1048,26 @@ final class UiModeManagerService extends SystemService {
|
||||
}
|
||||
|
||||
mCurUiMode = uiMode;
|
||||
if (!mHoldingConfiguration || !mWaitForScreenOff) {
|
||||
if (!mHoldingConfiguration && !mWaitForScreenOff) {
|
||||
mConfiguration.uiMode = uiMode;
|
||||
}
|
||||
}
|
||||
|
||||
@UiModeManager.NightMode
|
||||
private int getComputedUiModeConfiguration(@UiModeManager.NightMode int uiMode) {
|
||||
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
|
||||
: Configuration.UI_MODE_NIGHT_NO;
|
||||
uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO
|
||||
: ~Configuration.UI_MODE_NIGHT_YES;
|
||||
return uiMode;
|
||||
}
|
||||
|
||||
private boolean computeCustomNightMode() {
|
||||
return isTimeBetween(LocalTime.now(),
|
||||
mCustomAutoNightModeStartMilliseconds,
|
||||
mCustomAutoNightModeEndMilliseconds);
|
||||
}
|
||||
|
||||
private void applyConfigurationExternallyLocked() {
|
||||
if (mSetUiMode != mConfiguration.uiMode) {
|
||||
mSetUiMode = mConfiguration.uiMode;
|
||||
@@ -899,10 +1077,34 @@ final class UiModeManagerService extends SystemService {
|
||||
ActivityTaskManager.getService().updateConfiguration(mConfiguration);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failure communicating with activity manager", e);
|
||||
} catch (SecurityException e) {
|
||||
Slog.e(TAG, "Activity does not have the ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldApplyAutomaticChangesImmediately() {
|
||||
return mCar || !mPowerManager.isInteractive();
|
||||
}
|
||||
|
||||
private void scheduleNextCustomTimeListener() {
|
||||
cancelCustomAlarm();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
final boolean active = computeCustomNightMode();
|
||||
final LocalDateTime next = active
|
||||
? getDateTimeAfter(mCustomAutoNightModeEndMilliseconds, now)
|
||||
: getDateTimeAfter(mCustomAutoNightModeStartMilliseconds, now);
|
||||
final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, mCustomTimeListener, null);
|
||||
}
|
||||
|
||||
private LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
|
||||
final LocalDateTime ldt = LocalDateTime.of(compareTime.toLocalDate(), localTime);
|
||||
|
||||
// Check if the local time has passed, if so return the same time tomorrow.
|
||||
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
|
||||
}
|
||||
|
||||
void updateLocked(int enableFlags, int disableFlags) {
|
||||
String action = null;
|
||||
String oldAction = null;
|
||||
@@ -1133,26 +1335,21 @@ final class UiModeManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateComputedNightModeLocked() {
|
||||
if (mTwilightManager != null) {
|
||||
TwilightState state = mTwilightManager.getLastTwilightState();
|
||||
if (state != null) {
|
||||
mComputedNightMode = state.isNight();
|
||||
}
|
||||
if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) {
|
||||
mComputedNightMode = true;
|
||||
return;
|
||||
}
|
||||
if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) {
|
||||
mComputedNightMode = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mNightModeOverride = mNightMode;
|
||||
final int user = UserHandle.getCallingUserId();
|
||||
Secure.putIntForUser(getContext().getContentResolver(),
|
||||
OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
|
||||
private void updateComputedNightModeLocked(boolean activate) {
|
||||
mComputedNightMode = activate;
|
||||
if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) {
|
||||
mComputedNightMode = true;
|
||||
return;
|
||||
}
|
||||
if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) {
|
||||
mComputedNightMode = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mNightModeOverride = mNightMode;
|
||||
final int user = UserHandle.getCallingUserId();
|
||||
Secure.putIntForUser(getContext().getContentResolver(),
|
||||
OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
|
||||
}
|
||||
|
||||
private void registerVrStateListener() {
|
||||
@@ -1174,6 +1371,7 @@ final class UiModeManagerService extends SystemService {
|
||||
public static final String NIGHT_MODE_STR_YES = "yes";
|
||||
public static final String NIGHT_MODE_STR_NO = "no";
|
||||
public static final String NIGHT_MODE_STR_AUTO = "auto";
|
||||
public static final String NIGHT_MODE_STR_CUSTOM = "custom";
|
||||
public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
|
||||
private final IUiModeManager mInterface;
|
||||
|
||||
@@ -1246,6 +1444,8 @@ final class UiModeManagerService extends SystemService {
|
||||
return NIGHT_MODE_STR_NO;
|
||||
case UiModeManager.MODE_NIGHT_AUTO:
|
||||
return NIGHT_MODE_STR_AUTO;
|
||||
case MODE_NIGHT_CUSTOM:
|
||||
return NIGHT_MODE_STR_CUSTOM;
|
||||
default:
|
||||
return NIGHT_MODE_STR_UNKNOWN;
|
||||
}
|
||||
@@ -1259,6 +1459,8 @@ final class UiModeManagerService extends SystemService {
|
||||
return UiModeManager.MODE_NIGHT_NO;
|
||||
case NIGHT_MODE_STR_AUTO:
|
||||
return UiModeManager.MODE_NIGHT_AUTO;
|
||||
case NIGHT_MODE_STR_CUSTOM:
|
||||
return UiModeManager.MODE_NIGHT_CUSTOM;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -16,38 +16,54 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.IUiModeManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import com.android.server.twilight.TwilightManager;
|
||||
import com.android.server.twilight.TwilightState;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
|
||||
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
|
||||
import static android.app.UiModeManager.MODE_NIGHT_NO;
|
||||
import static android.app.UiModeManager.MODE_NIGHT_YES;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@@ -66,22 +82,51 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
|
||||
TwilightManager mTwilightManager;
|
||||
@Mock
|
||||
PowerManager.WakeLock mWakeLock;
|
||||
private Set<BroadcastReceiver> mScreenOffRecievers;
|
||||
@Mock
|
||||
AlarmManager mAlarmManager;
|
||||
@Mock
|
||||
PowerManager mPowerManager;
|
||||
@Mock
|
||||
TwilightState mTwilightState;
|
||||
|
||||
private BroadcastReceiver mScreenOffCallback;
|
||||
private BroadcastReceiver mTimeChangedCallback;
|
||||
private AlarmManager.OnAlarmListener mCustomListener;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mUiManagerService = new UiModeManagerService(mContext, mWindowManager, mWakeLock,
|
||||
mTwilightManager, true);
|
||||
mScreenOffRecievers = new HashSet<>();
|
||||
initMocks(this);
|
||||
mUiManagerService = new UiModeManagerService(mContext,
|
||||
mWindowManager, mAlarmManager, mPowerManager,
|
||||
mWakeLock, mTwilightManager, true);
|
||||
mService = mUiManagerService.getService();
|
||||
when(mContext.checkCallingOrSelfPermission(anyString()))
|
||||
.thenReturn(PackageManager.PERMISSION_GRANTED);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mContext.getContentResolver()).thenReturn(mContentResolver);
|
||||
when(mContext.registerReceiver(any(), any())).then(inv -> {
|
||||
mScreenOffRecievers.add(inv.getArgument(0));
|
||||
when(mPowerManager.isInteractive()).thenReturn(true);
|
||||
when(mTwilightManager.getLastTwilightState()).thenReturn(mTwilightState);
|
||||
when(mTwilightState.isNight()).thenReturn(true);
|
||||
when(mContext.registerReceiver(notNull(), notNull())).then(inv -> {
|
||||
IntentFilter filter = inv.getArgument(1);
|
||||
if (filter.hasAction(Intent.ACTION_TIMEZONE_CHANGED)) {
|
||||
mTimeChangedCallback = inv.getArgument(0);
|
||||
}
|
||||
if (filter.hasAction(Intent.ACTION_SCREEN_OFF)) {
|
||||
mScreenOffCallback = inv.getArgument(0);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
doAnswer(inv -> {
|
||||
mCustomListener = inv.getArgument(3);
|
||||
return null;
|
||||
}).when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(),
|
||||
any(AlarmManager.OnAlarmListener.class), any(Handler.class));
|
||||
|
||||
doAnswer(inv -> {
|
||||
mCustomListener = () -> {};
|
||||
return null;
|
||||
}).when(mAlarmManager).cancel(eq(mCustomListener));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -102,7 +147,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
|
||||
mService.setNightMode(MODE_NIGHT_NO);
|
||||
} catch (SecurityException e) { /*we should ignore this update config exception*/ }
|
||||
given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class);
|
||||
verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
|
||||
verify(mContext, atLeastOnce()).unregisterReceiver(any(BroadcastReceiver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,6 +210,132 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
|
||||
assertFalse(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_darkThemeOn() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_NO);
|
||||
mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertTrue(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_darkThemeOff() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_YES);
|
||||
mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertFalse(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_darkThemeOff_afterStartEnd() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_YES);
|
||||
mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertFalse(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_darkThemeOn_afterStartEnd() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_YES);
|
||||
mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertFalse(isNightModeActivated());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void customTime_darkThemeOn_beforeStartEnd() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_YES);
|
||||
mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.minusHours(2L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertTrue(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_darkThemeOff_beforeStartEnd() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_YES);
|
||||
mService.setCustomNightModeStart(now.minusHours(2L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertFalse(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTIme_customAlarmSetWhenScreenTimeChanges() throws RemoteException {
|
||||
when(mPowerManager.isInteractive()).thenReturn(false);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
verify(mAlarmManager, times(1))
|
||||
.setExact(anyInt(), anyLong(), anyString(), any(), any());
|
||||
mTimeChangedCallback.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
|
||||
verify(mAlarmManager, atLeast(2))
|
||||
.setExact(anyInt(), anyLong(), anyString(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_alarmSetInTheFutureWhenOn() throws RemoteException {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
when(mPowerManager.isInteractive()).thenReturn(false);
|
||||
mService.setNightMode(MODE_NIGHT_YES);
|
||||
mService.setCustomNightModeStart(now.toLocalTime().minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.toLocalTime().plusHours(1L).toNanoOfDay() / 1000);
|
||||
LocalDateTime next = now.plusHours(1L);
|
||||
final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
verify(mAlarmManager)
|
||||
.setExact(anyInt(), eq(millis), anyString(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_appliesImmediatelyWhenScreenOff() throws RemoteException {
|
||||
when(mPowerManager.isInteractive()).thenReturn(false);
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_NO);
|
||||
mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
assertTrue(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTime_appliesOnlyWhenScreenOff() throws RemoteException {
|
||||
LocalTime now = LocalTime.now();
|
||||
mService.setNightMode(MODE_NIGHT_NO);
|
||||
mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
|
||||
mService.setNightMode(MODE_NIGHT_CUSTOM);
|
||||
assertFalse(isNightModeActivated());
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertTrue(isNightModeActivated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nightAuto_appliesOnlyWhenScreenOff() throws RemoteException {
|
||||
when(mTwilightState.isNight()).thenReturn(true);
|
||||
mService.setNightMode(MODE_NIGHT_NO);
|
||||
mService.setNightMode(MODE_NIGHT_AUTO);
|
||||
assertFalse(isNightModeActivated());
|
||||
mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
|
||||
assertTrue(isNightModeActivated());
|
||||
}
|
||||
|
||||
private boolean isNightModeActivated() {
|
||||
return (mUiManagerService.getConfiguration().uiMode
|
||||
& Configuration.UI_MODE_NIGHT_YES) != 0;
|
||||
|
||||
Reference in New Issue
Block a user