Merge "Remove idle directory in SystemUI."

This commit is contained in:
Darrell Shi
2022-02-16 23:28:58 +00:00
committed by Android (Google) Code Review
14 changed files with 1 additions and 1551 deletions

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<com.android.systemui.idle.IdleHostView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/idle_host_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@@ -89,11 +89,6 @@
layout="@layout/keyguard_status_view"
android:visibility="gone"/>
<include layout="@layout/idle_host_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
<include layout="@layout/dock_info_overlay"/>
<FrameLayout

View File

@@ -20,8 +20,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -30,9 +28,6 @@ import com.android.systemui.communal.CommunalSource;
import com.android.systemui.communal.PackageObserver;
import com.android.systemui.communal.conditions.CommunalSettingCondition;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.idle.AmbientLightModeMonitor;
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.util.condition.Condition;
import com.android.systemui.util.condition.Monitor;
import com.android.systemui.util.condition.dagger.MonitorComponent;
@@ -46,7 +41,6 @@ import java.util.Set;
import javax.inject.Named;
import javax.inject.Provider;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ElementsIntoSet;
@@ -58,20 +52,10 @@ import dagger.multibindings.StringKey;
*/
@Module(subcomponents = {
CommunalViewComponent.class,
IdleViewComponent.class,
})
public interface CommunalModule {
String IDLE_VIEW = "idle_view";
String COMMUNAL_CONDITIONS = "communal_conditions";
/** */
@Provides
@Named(IDLE_VIEW)
static View provideIdleView(Context context) {
FrameLayout view = new FrameLayout(context);
return view;
}
/** */
@Provides
static Optional<CommunalSource.Observer> provideCommunalSourcePackageObserver(
@@ -86,15 +70,6 @@ public interface CommunalModule {
ComponentName.unflattenFromString(componentName).getPackageName()));
}
/**
* Provides LightSensorEventsDebounceAlgorithm as an instance to DebounceAlgorithm interface.
* @param algorithm the instance of algorithm that is bound to the interface.
* @return the interface that is bound to.
*/
@Binds
AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
LightSensorEventsDebounceAlgorithm algorithm);
/**
* Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
*/

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle
import android.annotation.IntDef
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
import com.android.systemui.util.sensors.AsyncSensorManager
import javax.inject.Inject
/**
* Monitors ambient light signals, applies a debouncing algorithm, and produces the current
* ambient light mode.
*
* @property algorithm the debounce algorithm which transforms light sensor events into an
* ambient light mode.
* @property sensorManager the sensor manager used to register sensor event updates.
*/
class AmbientLightModeMonitor @Inject constructor(
private val algorithm: DebounceAlgorithm,
private val sensorManager: AsyncSensorManager
) {
companion object {
private const val TAG = "AmbientLightModeMonitor"
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
const val AMBIENT_LIGHT_MODE_LIGHT = 0
const val AMBIENT_LIGHT_MODE_DARK = 1
const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
}
// Light sensor used to detect ambient lighting conditions.
private val lightSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
// Represents all ambient light modes.
@Retention(AnnotationRetention.SOURCE)
@IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
annotation class AmbientLightMode
/**
* Start monitoring the current ambient light mode.
*
* @param callback callback that gets triggered when the ambient light mode changes.
*/
fun start(callback: Callback) {
if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
if (lightSensor == null) {
if (DEBUG) Log.w(TAG, "light sensor not available")
return
}
algorithm.start(callback)
sensorManager.registerListener(mSensorEventListener, lightSensor,
SensorManager.SENSOR_DELAY_NORMAL)
}
/**
* Stop monitoring the current ambient light mode.
*/
fun stop() {
if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
algorithm.stop()
sensorManager.unregisterListener(mSensorEventListener)
}
private val mSensorEventListener: SensorEventListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
if (event.values.isEmpty()) {
if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value")
return
}
algorithm.onUpdateLightSensorEvent(event.values[0])
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
// Do nothing.
}
}
/**
* Interface of the ambient light mode callback, which gets triggered when the mode changes.
*/
interface Callback {
fun onChange(@AmbientLightMode mode: Int)
}
/**
* Interface of the algorithm that transforms light sensor events to an ambient light mode.
*/
interface DebounceAlgorithm {
// Setting Callback to nullable so mockito can verify without throwing NullPointerException.
fun start(callback: Callback?)
fun stop()
fun onUpdateLightSensorEvent(value: Float)
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* {@link IdleHostView} houses a surface to be displayed when the device idle.
*/
public class IdleHostView extends FrameLayout {
public IdleHostView(@NonNull Context context) {
this(context, null);
}
public IdleHostView(@NonNull Context context,
@Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public IdleHostView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}

View File

@@ -1,329 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle;
import static com.android.systemui.communal.dagger.CommunalModule.IDLE_VIEW;
import android.annotation.IntDef;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
/**
* {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen.
*/
public class IdleHostViewController extends ViewController<IdleHostView> {
private static final String TAG = "IdleHostViewController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Retention(RetentionPolicy.RUNTIME)
@IntDef({STATE_IDLE_MODE_ENABLED, STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_DREAMING,
STATE_LOW_LIGHT})
public @interface State {}
// Set at construction to indicate idle mode is available.
private static final int STATE_IDLE_MODE_ENABLED = 1 << 0;
// Set when keyguard is present, even below a dream or AOD. AKA the device is locked.
private static final int STATE_KEYGUARD_SHOWING = 1 << 1;
// Set when the device has entered a dozing / low power state.
private static final int STATE_DOZING = 1 << 2;
// Set when the device has entered a dreaming state, which includes dozing.
private static final int STATE_DREAMING = 1 << 3;
// Set when the device is in a low light environment.
private static final int STATE_LOW_LIGHT = 1 << 4;
// The aggregate current state.
private int mState;
private boolean mIsMonitoringLowLight;
private boolean mIsMonitoringDream;
private final BroadcastDispatcher mBroadcastDispatcher;
private final PowerManager mPowerManager;
// Keyguard state controller for monitoring keyguard show state.
private final KeyguardStateController mKeyguardStateController;
// Status bar state controller for monitoring when the device is dozing.
private final StatusBarStateController mStatusBarStateController;
// Intent filter for receiving dream broadcasts.
private IntentFilter mDreamIntentFilter;
// Monitor for the current ambient light mode. Used to trigger / exit low-light mode.
private final AmbientLightModeMonitor mAmbientLightModeMonitor;
private final KeyguardStateController.Callback mKeyguardCallback =
new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
}
};
private final StatusBarStateController.StateListener mStatusBarCallback =
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
setState(STATE_DOZING, isDozing);
}
};
private final BroadcastReceiver mDreamStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
setState(STATE_DREAMING, true);
} else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
setState(STATE_DREAMING, false);
}
}
};
private final AmbientLightModeMonitor.Callback mAmbientLightModeCallback =
mode -> {
boolean shouldBeLowLight;
switch (mode) {
case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED:
return;
case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT:
shouldBeLowLight = false;
break;
case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK:
shouldBeLowLight = true;
break;
default:
Log.w(TAG, "invalid ambient light mode");
return;
}
if (DEBUG) Log.d(TAG, "ambient light mode changed to " + mode);
final boolean isLowLight = getState(STATE_LOW_LIGHT);
if (shouldBeLowLight != isLowLight) {
setState(STATE_LOW_LIGHT, shouldBeLowLight);
}
};
final Provider<View> mIdleViewProvider;
@Inject
protected IdleHostViewController(
BroadcastDispatcher broadcastDispatcher,
PowerManager powerManager,
IdleHostView view,
@Main Resources resources,
@Named(IDLE_VIEW) Provider<View> idleViewProvider,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
AmbientLightModeMonitor ambientLightModeMonitor) {
super(view);
mBroadcastDispatcher = broadcastDispatcher;
mPowerManager = powerManager;
mIdleViewProvider = idleViewProvider;
mKeyguardStateController = keyguardStateController;
mStatusBarStateController = statusBarStateController;
mAmbientLightModeMonitor = ambientLightModeMonitor;
mState = STATE_KEYGUARD_SHOWING;
final boolean enabled = resources.getBoolean(R.bool.config_enableIdleMode);
if (enabled) {
mState |= STATE_IDLE_MODE_ENABLED;
}
setState(mState, true);
if (DEBUG) {
Log.d(TAG, "initial state:" + mState + " enabled:" + enabled);
}
}
@Override
public void init() {
super.init();
setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
setState(STATE_DOZING, mStatusBarStateController.isDozing());
}
private void setState(@State int state, boolean active) {
final int oldState = mState;
if (active) {
mState |= state;
} else {
mState &= ~state;
}
if (oldState == mState) {
return;
}
if (DEBUG) {
Log.d(TAG, "set " + getStateName(state) + " to " + active);
logCurrentState();
}
final boolean inCommunalMode = getState(STATE_IDLE_MODE_ENABLED)
&& getState(STATE_KEYGUARD_SHOWING);
enableDreamMonitoring(inCommunalMode);
enableLowLightMonitoring(inCommunalMode);
if (state == STATE_LOW_LIGHT) {
enableLowLightMode(inCommunalMode && active);
}
}
private void enableDreamMonitoring(boolean enable) {
if (mIsMonitoringDream == enable) {
return;
}
mIsMonitoringDream = enable;
if (DEBUG) {
Log.d(TAG, (enable ? "enable" : "disable") + " dream monitoring");
}
if (mDreamIntentFilter == null) {
mDreamIntentFilter = new IntentFilter();
mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
}
if (enable) {
mBroadcastDispatcher.registerReceiver(mDreamStateReceiver, mDreamIntentFilter);
} else {
mBroadcastDispatcher.unregisterReceiver(mDreamStateReceiver);
}
}
private void enableLowLightMonitoring(boolean enable) {
if (enable == mIsMonitoringLowLight) {
return;
}
mIsMonitoringLowLight = enable;
if (mIsMonitoringLowLight) {
if (DEBUG) Log.d(TAG, "enable low light monitoring");
mAmbientLightModeMonitor.start(mAmbientLightModeCallback);
} else {
if (DEBUG) Log.d(TAG, "disable low light monitoring");
mAmbientLightModeMonitor.stop();
}
}
private void enableLowLightMode(boolean enable) {
if (enable == getState(STATE_DOZING)) {
return;
}
if (enable) {
if (DEBUG) Log.d(TAG, "enter low light, start dozing");
mPowerManager.goToSleep(
SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
} else {
if (DEBUG) Log.d(TAG, "exit low light, stop dozing");
mPowerManager.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION, "Exit low light condition");
}
}
@Override
protected void onViewAttached() {
if (DEBUG) {
Log.d(TAG, "onViewAttached");
}
mKeyguardStateController.addCallback(mKeyguardCallback);
mStatusBarStateController.addCallback(mStatusBarCallback);
}
@Override
protected void onViewDetached() {
mKeyguardStateController.removeCallback(mKeyguardCallback);
mStatusBarStateController.removeCallback(mStatusBarCallback);
}
private String getStateName(@State int state) {
switch (state) {
case STATE_IDLE_MODE_ENABLED:
return "STATE_IDLE_MODE_ENABLED";
case STATE_KEYGUARD_SHOWING:
return "STATE_KEYGUARD_SHOWING";
case STATE_DOZING:
return "STATE_DOZING";
case STATE_DREAMING:
return "STATE_DREAMING";
case STATE_LOW_LIGHT:
return "STATE_LOW_LIGHT";
default:
return "STATE_UNKNOWN";
}
}
private boolean getState(@State int state) {
return getState(state, mState);
}
private boolean getState(@State int state, int oldState) {
return (oldState & state) == state;
}
private String getStateLog(@State int state) {
return getStateName(state) + " = " + getState(state);
}
private void logCurrentState() {
Log.d(TAG, "current state: {\n"
+ "\t" + getStateLog(STATE_IDLE_MODE_ENABLED) + "\n"
+ "\t" + getStateLog(STATE_KEYGUARD_SHOWING) + "\n"
+ "\t" + getStateLog(STATE_DOZING) + "\n"
+ "\t" + getStateLog(STATE_DREAMING) + "\n"
+ "\t" + getStateLog(STATE_LOW_LIGHT) + "\n"
+ "}");
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.shared.system.InputMonitorCompat;
import javax.inject.Inject;
/**
* {@link InputMonitorFactory} generates instances of {@link InputMonitorCompat}.
*/
public class InputMonitorFactory {
private final int mDisplayId;
@Inject
public InputMonitorFactory(@DisplayId int displayId) {
mDisplayId = displayId;
}
/**
* Generates a new {@link InputMonitorCompat}.
*
* @param identifier Identifier to generate monitor with.
* @return A {@link InputMonitorCompat} instance.
*/
public InputMonitorCompat getInputMonitor(String identifier) {
return new InputMonitorCompat(identifier, mDisplayId);
}
}

View File

@@ -1,284 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle
import android.content.res.Resources
import android.util.Log
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
* An algorithm that receives light sensor events, debounces the signals, and transforms into an
* ambient light mode: light, dark, or undecided.
*
* More about the algorithm at go/titan-light-sensor-debouncer.
*/
class LightSensorEventsDebounceAlgorithm @Inject constructor(
@Main private val executor: DelayableExecutor,
@Main resources: Resources
) : AmbientLightModeMonitor.DebounceAlgorithm {
companion object {
private const val TAG = "LightDebounceAlgorithm"
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
}
// The ambient mode is considered light mode when the light sensor value increases exceeding
// this value.
private val lightModeThreshold =
resources.getInteger(R.integer.config_ambientLightModeThreshold)
// The ambient mode is considered dark mode when the light sensor value drops below this
// value.
private val darkModeThreshold = resources.getInteger(R.integer.config_ambientDarkModeThreshold)
// Each sample for calculating light mode contains light sensor events collected for this
// duration of time in milliseconds.
private val lightSamplingSpanMillis =
resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis)
// Each sample for calculating dark mode contains light sensor events collected for this
// duration of time in milliseconds.
private val darkSamplingSpanMillis =
resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis)
// The calculations for light mode is performed at this frequency in milliseconds.
private val lightSamplingFrequencyMillis =
resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis)
// The calculations for dark mode is performed at this frequency in milliseconds.
private val darkSamplingFrequencyMillis =
resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis)
// Registered callback, which gets triggered when the ambient light mode changes.
private var callback: AmbientLightModeMonitor.Callback? = null
// Queue of bundles used for calculating [isLightMode], ordered from oldest to latest.
val bundlesQueueLightMode = ArrayList<ArrayList<Float>>()
// Queue of bundles used for calculating [isDarkMode], ordered from oldest to latest
val bundlesQueueDarkMode = ArrayList<ArrayList<Float>>()
// The current ambient light mode.
@AmbientLightModeMonitor.AmbientLightMode
var mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
set(value) {
if (field == value) return
field = value
if (DEBUG) Log.d(TAG, "ambient light mode changed to $value")
callback?.onChange(value)
}
// The latest claim of whether it should be light mode.
var isLightMode = false
set(value) {
if (field == value) return
field = value
if (DEBUG) Log.d(TAG, "isLightMode: $value")
mode = when {
isDarkMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
}
}
// The latest claim of whether it should be dark mode.
var isDarkMode = false
set(value) {
if (field == value) return
field = value
if (DEBUG) Log.d(TAG, "isDarkMode: $value")
mode = when {
value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
isLightMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
}
}
// The latest average of the light mode bundle.
var bundleAverageLightMode = 0.0
set(value) {
field = value
if (DEBUG) Log.d(TAG, "light mode average: $value")
isLightMode = value > lightModeThreshold
}
// The latest average of the dark mode bundle.
var bundleAverageDarkMode = 0.0
set(value) {
field = value
if (DEBUG) Log.d(TAG, "dark mode average: $value")
isDarkMode = value < darkModeThreshold
}
// The latest bundle for calculating light mode claim.
var bundleLightMode = ArrayList<Float>()
set(value) {
field = value
val average = value.average()
if (!average.isNaN()) {
bundleAverageLightMode = average
}
}
// The latest bundle for calculating dark mode claim.
var bundleDarkMode = ArrayList<Float>()
set(value) {
field = value
val average = value.average()
if (!average.isNaN()) {
bundleAverageDarkMode = average
}
}
// The latest light level from light sensor event updates.
var lightSensorLevel = 0.0f
set(value) {
field = value
bundlesQueueLightMode.forEach { bundle -> bundle.add(value) }
bundlesQueueDarkMode.forEach { bundle -> bundle.add(value) }
}
// Creates a new bundle that collects light sensor events for calculating the light mode claim,
// and adds it to the end of the queue. It schedules a call to dequeue this bundle after
// [LIGHT_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
// [LIGHT_SAMPLING_FREQUENCY_MILLIS].
val enqueueLightModeBundle: Runnable = object : Runnable {
override fun run() {
if (DEBUG) Log.d(TAG, "enqueueing a light mode bundle")
bundlesQueueLightMode.add(ArrayList())
executor.executeDelayed(dequeueLightModeBundle, lightSamplingSpanMillis.toLong())
executor.executeDelayed(this, lightSamplingFrequencyMillis.toLong())
}
}
// Creates a new bundle that collects light sensor events for calculating the dark mode claim,
// and adds it to the end of the queue. It schedules a call to dequeue this bundle after
// [DARK_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
// [DARK_SAMPLING_FREQUENCY_MILLIS].
val enqueueDarkModeBundle: Runnable = object : Runnable {
override fun run() {
if (DEBUG) Log.d(TAG, "enqueueing a dark mode bundle")
bundlesQueueDarkMode.add(ArrayList())
executor.executeDelayed(dequeueDarkModeBundle, darkSamplingSpanMillis.toLong())
executor.executeDelayed(this, darkSamplingFrequencyMillis.toLong())
}
}
// Collects the oldest bundle from the light mode bundles queue, and as a result triggering a
// calculation of the light mode claim.
val dequeueLightModeBundle: Runnable = object : Runnable {
override fun run() {
if (bundlesQueueLightMode.isEmpty()) return
bundleLightMode = bundlesQueueLightMode.removeAt(0)
if (DEBUG) Log.d(TAG, "dequeued a light mode bundle of size ${bundleLightMode.size}")
}
}
// Collects the oldest bundle from the dark mode bundles queue, and as a result triggering a
// calculation of the dark mode claim.
val dequeueDarkModeBundle: Runnable = object : Runnable {
override fun run() {
if (bundlesQueueDarkMode.isEmpty()) return
bundleDarkMode = bundlesQueueDarkMode.removeAt(0)
if (DEBUG) Log.d(TAG, "dequeued a dark mode bundle of size ${bundleDarkMode.size}")
}
}
/**
* Start the algorithm.
*
* @param callback callback that gets triggered when the ambient light mode changes.
*/
override fun start(callback: AmbientLightModeMonitor.Callback?) {
if (DEBUG) Log.d(TAG, "start algorithm")
if (callback == null) {
if (DEBUG) Log.w(TAG, "callback is null")
return
}
if (this.callback != null) {
if (DEBUG) Log.w(TAG, "already started")
return
}
this.callback = callback
executor.execute(enqueueLightModeBundle)
executor.execute(enqueueDarkModeBundle)
}
/**
* Stop the algorithm.
*/
override fun stop() {
if (DEBUG) Log.d(TAG, "stop algorithm")
if (callback == null) {
if (DEBUG) Log.w(TAG, "haven't started")
return
}
callback = null
// Resets bundle queues.
bundlesQueueLightMode.clear()
bundlesQueueDarkMode.clear()
// Resets ambient light mode.
mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
}
/**
* Update the light sensor event value.
*
* @param value light sensor update value.
*/
override fun onUpdateLightSensorEvent(value: Float) {
if (callback == null) {
if (DEBUG) Log.w(TAG, "ignore light sensor event because algorithm not started")
return
}
lightSensorLevel = value
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle.dagger;
import com.android.systemui.idle.IdleHostView;
import com.android.systemui.idle.IdleHostViewController;
import dagger.BindsInstance;
import dagger.Subcomponent;
/**
* Subcomponent for working with {@link IdleHostView}.
*/
@Subcomponent
public interface IdleViewComponent {
/** Simple factory for {@link Factory}. */
@Subcomponent.Factory
interface Factory {
IdleViewComponent build(@BindsInstance IdleHostView idleHostView);
}
/** Builds a {@link IdleHostViewController}. */
IdleHostViewController getIdleHostViewController();
}

View File

@@ -135,9 +135,6 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostView;
import com.android.systemui.idle.IdleHostViewController;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
@@ -322,7 +319,6 @@ public class NotificationPanelViewController extends PanelViewController
private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
private final IdleViewComponent.Factory mIdleViewComponentFactory;
private final FragmentService mFragmentService;
private final ScrimController mScrimController;
private final PrivacyDotViewController mPrivacyDotViewController;
@@ -356,8 +352,6 @@ public class NotificationPanelViewController extends PanelViewController
@Nullable
private CommunalHostViewController mCommunalViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
@Nullable
private IdleHostViewController mIdleHostViewController;
private LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationsQSContainerController mNotificationsQSContainerController;
@@ -369,7 +363,6 @@ public class NotificationPanelViewController extends PanelViewController
private VelocityTracker mQsVelocityTracker;
private boolean mQsTracking;
private IdleHostView mIdleHostView;
private CommunalHostView mCommunalView;
/**
@@ -768,7 +761,6 @@ public class NotificationPanelViewController extends PanelViewController
KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
CommunalViewComponent.Factory communalViewComponentFactory,
IdleViewComponent.Factory idleViewComponentFactory,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
@@ -839,7 +831,6 @@ public class NotificationPanelViewController extends PanelViewController
mCommunalViewComponentFactory = communalViewComponentFactory;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
mIdleViewComponentFactory = idleViewComponentFactory;
mDepthController = notificationShadeDepthController;
mContentResolver = contentResolver;
mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
@@ -966,7 +957,6 @@ public class NotificationPanelViewController extends PanelViewController
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
mIdleHostView = mView.findViewById(R.id.idle_host_view);
mCommunalView = mView.findViewById(R.id.communal_host);
FrameLayout userAvatarContainer = null;
@@ -989,12 +979,6 @@ public class NotificationPanelViewController extends PanelViewController
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
if (mIdleHostView != null) {
IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(mIdleHostView);
mIdleHostViewController = idleViewComponent.getIdleHostViewController();
mIdleHostViewController.init();
}
if (mCommunalView != null) {
CommunalViewComponent communalViewComponent =
mCommunalViewComponentFactory.build(mCommunalView);
@@ -1008,7 +992,6 @@ public class NotificationPanelViewController extends PanelViewController
mView.findViewById(R.id.keyguard_status_view),
userAvatarContainer,
keyguardUserSwitcherView,
mIdleHostView,
mCommunalView);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -1101,7 +1084,6 @@ public class NotificationPanelViewController extends PanelViewController
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView,
IdleHostView idleHostView,
CommunalHostView communalView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
@@ -1109,13 +1091,6 @@ public class NotificationPanelViewController extends PanelViewController
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
if (idleHostView != null && idleHostView != mIdleHostView) {
mIdleHostView = idleHostView;
IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
mIdleHostViewController = idleViewComponent.getIdleHostViewController();
mIdleHostViewController.init();
}
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
// Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1287,7 +1262,7 @@ public class NotificationPanelViewController extends PanelViewController
showKeyguardUserSwitcher /* enabled */);
updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
keyguardUserSwitcherView, mCommunalView);
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);

View File

@@ -1,111 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle
import android.hardware.Sensor
import android.hardware.SensorEventListener
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.sensors.AsyncSensorManager
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
class AmbientLightModeMonitorTest : SysuiTestCase() {
@Mock private lateinit var sensorManager: AsyncSensorManager
@Mock private lateinit var sensor: Sensor
@Mock private lateinit var algorithm: AmbientLightModeMonitor.DebounceAlgorithm
private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(sensor)
ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
}
@Test
fun shouldRegisterSensorEventListenerOnStart() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
ambientLightModeMonitor.start(callback)
verify(sensorManager).registerListener(any(), eq(sensor), anyInt())
}
@Test
fun shouldUnregisterSensorEventListenerOnStop() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
ambientLightModeMonitor.start(callback)
val sensorEventListener = captureSensorEventListener()
ambientLightModeMonitor.stop()
verify(sensorManager).unregisterListener(eq(sensorEventListener))
}
@Test
fun shouldStartDebounceAlgorithmOnStart() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
ambientLightModeMonitor.start(callback)
verify(algorithm).start(eq(callback))
}
@Test
fun shouldStopDebounceAlgorithmOnStop() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
ambientLightModeMonitor.start(callback)
ambientLightModeMonitor.stop()
verify(algorithm).stop()
}
@Test
fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
`when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(null)
val ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
ambientLightModeMonitor.start(callback)
verify(sensorManager, never()).registerListener(any(), any(Sensor::class.java), anyInt())
}
// Captures [SensorEventListener], assuming it has been registered with [sensorManager].
private fun captureSensorEventListener(): SensorEventListener {
val captor = ArgumentCaptor.forClass(SensorEventListener::class.java)
verify(sensorManager).registerListener(captor.capture(), any(), anyInt())
return captor.value
}
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.inject.Provider;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class IdleHostViewControllerTest extends SysuiTestCase {
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private PowerManager mPowerManager;
@Mock private IdleHostView mIdleHostView;
@Mock private Resources mResources;
@Mock private Provider<View> mViewProvider;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private AmbientLightModeMonitor mAmbientLightModeMonitor;
private KeyguardStateController.Callback mKeyguardStateCallback;
private StatusBarStateController.StateListener mStatusBarStateListener;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true);
when(mStatusBarStateController.isDozing()).thenReturn(false);
final IdleHostViewController controller = new IdleHostViewController(mBroadcastDispatcher,
mPowerManager, mIdleHostView, mResources, mViewProvider, mKeyguardStateController,
mStatusBarStateController, mAmbientLightModeMonitor);
controller.init();
controller.onViewAttached();
// Captures keyguard state controller callback.
ArgumentCaptor<KeyguardStateController.Callback> keyguardStateCallbackCaptor =
ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
verify(mKeyguardStateController).addCallback(keyguardStateCallbackCaptor.capture());
mKeyguardStateCallback = keyguardStateCallbackCaptor.getValue();
// Captures status bar state listener.
ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
mStatusBarStateListener = statusBarStateListenerCaptor.getValue();
}
@Test
public void testStartDozingWhenLowLight() {
// Keyguard showing.
when(mKeyguardStateController.isShowing()).thenReturn(true);
mKeyguardStateCallback.onKeyguardShowingChanged();
// Regular ambient lighting.
final AmbientLightModeMonitor.Callback lightMonitorCallback =
captureAmbientLightModeMonitorCallback();
lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
// Verifies it doesn't go to sleep yet.
verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
// Ambient lighting becomes dim.
lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
// Verifies it goes to sleep.
verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
}
@Test
public void testWakeUpWhenRegularLight() {
// Keyguard showing.
when(mKeyguardStateController.isShowing()).thenReturn(true);
mKeyguardStateCallback.onKeyguardShowingChanged();
// In low light / dozing.
final AmbientLightModeMonitor.Callback lightMonitorCallback =
captureAmbientLightModeMonitorCallback();
lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
// Regular ambient lighting.
lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
// Verifies it wakes up from sleep.
verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
}
// Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor
// has been started.
private AmbientLightModeMonitor.Callback captureAmbientLightModeMonitorCallback() {
ArgumentCaptor<AmbientLightModeMonitor.Callback> captor =
ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class);
verify(mAmbientLightModeMonitor).start(captor.capture());
return captor.getValue();
}
}

View File

@@ -1,358 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.idle
import android.content.res.Resources
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.reset
import org.mockito.Mockito.`when`
@SmallTest
@RunWith(AndroidTestingRunner::class)
class LightSensorDebounceAlgorithmTest : SysuiTestCase() {
@Mock private lateinit var resources: Resources
private val systemClock = FakeSystemClock()
private val executor = FakeExecutor(systemClock)
private lateinit var algorithm: LightSensorEventsDebounceAlgorithm
private val mockLightModeThreshold = 5
private val mockDarkModeThreshold = 2
private val mockLightModeSpan = 100
private val mockDarkModeSpan = 50
private val mockLightModeFrequency = 10
private val mockDarkModeFrequency = 5
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(resources.getInteger(R.integer.config_ambientLightModeThreshold))
.thenReturn(mockLightModeThreshold)
`when`(resources.getInteger(R.integer.config_ambientDarkModeThreshold))
.thenReturn(mockDarkModeThreshold)
`when`(resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis))
.thenReturn(mockLightModeSpan)
`when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis))
.thenReturn(mockDarkModeSpan)
`when`(resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis))
.thenReturn(mockLightModeFrequency)
`when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis))
.thenReturn(mockDarkModeFrequency)
algorithm = LightSensorEventsDebounceAlgorithm(executor, resources)
}
@Test
fun shouldOnlyTriggerCallbackWhenValueChanges() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
algorithm.start(callback)
// Light mode, should trigger callback.
algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
reset(callback)
// Light mode again, should NOT trigger callback.
algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
reset(callback)
// Dark mode, should trigger callback.
algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
reset(callback)
// Dark mode again, should not trigger callback.
algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
}
@Test
fun shouldReportUndecidedWhenNeitherLightNorDarkClaimIsTrue() {
algorithm.isDarkMode = false
algorithm.isLightMode = false
assertThat(algorithm.mode).isEqualTo(
AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
}
@Test
fun shouldReportDarkModeAsLongAsDarkModeClaimIsTrue() {
algorithm.isDarkMode = true
algorithm.isLightMode = false
assertThat(algorithm.mode).isEqualTo(
AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
algorithm.isLightMode = true
assertThat(algorithm.mode).isEqualTo(
AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
}
@Test
fun shouldReportLightModeWhenLightModeClaimIsTrueAndDarkModeClaimIsFalse() {
algorithm.isLightMode = true
algorithm.isDarkMode = false
assertThat(algorithm.mode).isEqualTo(
AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
}
@Test
fun shouldSetIsLightModeToTrueWhenBundleAverageIsGreaterThanThreshold() {
// Note: [mockLightModeThreshold] is 5.0.
algorithm.bundleAverageLightMode = 5.1
assertThat(algorithm.isLightMode).isTrue()
algorithm.bundleAverageLightMode = 10.0
assertThat(algorithm.isLightMode).isTrue()
algorithm.bundleAverageLightMode = 20.0
assertThat(algorithm.isLightMode).isTrue()
algorithm.bundleAverageLightMode = 5.0
assertThat(algorithm.isLightMode).isFalse()
algorithm.bundleAverageLightMode = 3.0
assertThat(algorithm.isLightMode).isFalse()
algorithm.bundleAverageLightMode = 0.0
assertThat(algorithm.isLightMode).isFalse()
}
@Test
fun shouldSetIsDarkModeToTrueWhenBundleAverageIsLessThanThreshold() {
// Note: [mockDarkModeThreshold] is 2.0.
algorithm.bundleAverageDarkMode = 1.9
assertThat(algorithm.isDarkMode).isTrue()
algorithm.bundleAverageDarkMode = 1.0
assertThat(algorithm.isDarkMode).isTrue()
algorithm.bundleAverageDarkMode = 0.0
assertThat(algorithm.isDarkMode).isTrue()
algorithm.bundleAverageDarkMode = 2.0
assertThat(algorithm.isDarkMode).isFalse()
algorithm.bundleAverageDarkMode = 3.0
assertThat(algorithm.isDarkMode).isFalse()
algorithm.bundleAverageDarkMode = 10.0
assertThat(algorithm.isDarkMode).isFalse()
}
@Test
fun shouldCorrectlyCalculateAverageFromABundle() {
// For light mode.
algorithm.bundleLightMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f)
assertThat(algorithm.bundleAverageLightMode).isEqualTo(4.0)
algorithm.bundleLightMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f)
assertThat(algorithm.bundleAverageLightMode).isEqualTo(5.0)
// For dark mode.
algorithm.bundleDarkMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f, 9.0f)
assertThat(algorithm.bundleAverageDarkMode).isEqualTo(5.0)
algorithm.bundleDarkMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f, 10.0f)
assertThat(algorithm.bundleAverageDarkMode).isEqualTo(6.0)
}
@Test
fun shouldAddSensorEventUpdatesToBundles() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
// On start() one bundle is created for light and dark mode each.
algorithm.start(callback)
executor.runAllReady()
// Add 1 more bundle to queue for each mode.
algorithm.bundlesQueueLightMode.add(ArrayList())
algorithm.bundlesQueueDarkMode.add(ArrayList())
algorithm.onUpdateLightSensorEvent(1.0f)
algorithm.onUpdateLightSensorEvent(1.0f)
algorithm.onUpdateLightSensorEvent(2.0f)
algorithm.onUpdateLightSensorEvent(3.0f)
algorithm.onUpdateLightSensorEvent(4.0f)
val expectedValues = listOf(1.0f, 1.0f, 2.0f, 3.0f, 4.0f)
assertBundleContainsAll(algorithm.bundlesQueueLightMode[0], expectedValues)
assertBundleContainsAll(algorithm.bundlesQueueLightMode[1], expectedValues)
assertBundleContainsAll(algorithm.bundlesQueueDarkMode[0], expectedValues)
assertBundleContainsAll(algorithm.bundlesQueueDarkMode[1], expectedValues)
}
@Test
fun shouldCorrectlyEnqueueLightModeBundles() {
assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
algorithm.enqueueLightModeBundle.run()
assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(1)
algorithm.enqueueLightModeBundle.run()
assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(2)
algorithm.enqueueLightModeBundle.run()
assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(3)
// Verifies dark mode bundles queue is not impacted.
assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
}
@Test
fun shouldCorrectlyEnqueueDarkModeBundles() {
assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
algorithm.enqueueDarkModeBundle.run()
assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(1)
algorithm.enqueueDarkModeBundle.run()
assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(2)
algorithm.enqueueDarkModeBundle.run()
assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(3)
// Verifies light mode bundles queue is not impacted.
assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
}
@Test
fun shouldCorrectlyDequeueLightModeBundles() {
// Sets up the light mode bundles queue.
val bundle1 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
val bundle2 = arrayListOf(5.0f, 10f)
val bundle3 = arrayListOf(2.0f, 4.0f)
algorithm.bundlesQueueLightMode.add(bundle1)
algorithm.bundlesQueueLightMode.add(bundle2)
algorithm.bundlesQueueLightMode.add(bundle3)
// The committed bundle should be the first one in queue.
algorithm.dequeueLightModeBundle.run()
assertBundleContainsAll(algorithm.bundleLightMode, bundle1)
algorithm.dequeueLightModeBundle.run()
assertBundleContainsAll(algorithm.bundleLightMode, bundle2)
algorithm.dequeueLightModeBundle.run()
assertBundleContainsAll(algorithm.bundleLightMode, bundle3)
// Verifies that the dark mode bundle is not impacted.
assertBundleContainsAll(algorithm.bundleDarkMode, listOf())
}
@Test
fun shouldCorrectlyDequeueDarkModeBundles() {
// Sets up the dark mode bundles queue.
val bundle1 = arrayListOf(2.0f, 4.0f)
val bundle2 = arrayListOf(5.0f, 10f)
val bundle3 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
algorithm.bundlesQueueDarkMode.add(bundle1)
algorithm.bundlesQueueDarkMode.add(bundle2)
algorithm.bundlesQueueDarkMode.add(bundle3)
// The committed bundle should be the first one in queue.
algorithm.dequeueDarkModeBundle.run()
assertBundleContainsAll(algorithm.bundleDarkMode, bundle1)
algorithm.dequeueDarkModeBundle.run()
assertBundleContainsAll(algorithm.bundleDarkMode, bundle2)
algorithm.dequeueDarkModeBundle.run()
assertBundleContainsAll(algorithm.bundleDarkMode, bundle3)
// Verifies that the light mode bundle is not impacted.
assertBundleContainsAll(algorithm.bundleLightMode, listOf())
}
@Test
fun shouldSetLightSensorLevelFromSensorEventUpdates() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
algorithm.start(callback)
algorithm.onUpdateLightSensorEvent(1.0f)
assertThat(algorithm.lightSensorLevel).isEqualTo(1.0f)
algorithm.onUpdateLightSensorEvent(10.0f)
assertThat(algorithm.lightSensorLevel).isEqualTo(10.0f)
algorithm.onUpdateLightSensorEvent(0.0f)
assertThat(algorithm.lightSensorLevel).isEqualTo(0.0f)
}
@Test
fun shouldRippleFromSensorEventUpdatesDownToAmbientLightMode() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
algorithm.start(callback)
executor.runAllReady()
// Sensor event updates.
algorithm.onUpdateLightSensorEvent(10.0f)
algorithm.onUpdateLightSensorEvent(15.0f)
algorithm.onUpdateLightSensorEvent(12.0f)
algorithm.onUpdateLightSensorEvent(10.0f)
// Advances time so both light and dark claims have been calculated.
systemClock.advanceTime((mockLightModeSpan + 1).toLong())
// Verifies the callback is triggered the ambient mode has changed LIGHT.
verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
}
@Test
fun shouldRippleFromSensorEventUpdatesDownToAmbientDarkMode() {
val callback = mock(AmbientLightModeMonitor.Callback::class.java)
algorithm.start(callback)
executor.runAllReady()
// Sensor event updates.
algorithm.onUpdateLightSensorEvent(1.0f)
algorithm.onUpdateLightSensorEvent(0.5f)
algorithm.onUpdateLightSensorEvent(1.2f)
algorithm.onUpdateLightSensorEvent(0.8f)
// Advances time so both light and dark claims have been calculated.
systemClock.advanceTime((mockLightModeSpan + 1).toLong())
// Verifies the callback is triggered the ambient mode has changed DARK.
verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
}
// Asserts that [bundle] contains the same elements as [expected], not necessarily in the same
// order.
private fun assertBundleContainsAll(bundle: ArrayList<Float>, expected: Collection<Float>) {
assertThat(bundle.size).isEqualTo(expected.size)
assertThat(bundle.containsAll(expected))
}
}

View File

@@ -100,8 +100,6 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostViewController;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
@@ -266,12 +264,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@Mock
private IdleViewComponent.Factory mIdleViewComponentFactory;
@Mock
private IdleViewComponent mIdleViewComponent;
@Mock
private IdleHostViewController mIdleHostViewController;
@Mock
private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock
private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@@ -475,10 +467,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.thenReturn(mCommunalViewComponent);
when(mCommunalViewComponent.getCommunalHostViewController())
.thenReturn(mCommunalHostViewController);
when(mIdleViewComponentFactory.build(any()))
.thenReturn(mIdleViewComponent);
when(mIdleViewComponent.getIdleHostViewController())
.thenReturn(mIdleHostViewController);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
.thenReturn(mKeyguardStatusView);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
@@ -523,7 +511,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
mCommunalViewComponentFactory,
mIdleViewComponentFactory,
mLockscreenShadeTransitionController,
mGroupManager,
mNotificationAreaController,