summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/src')
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java142
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java299
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java325
51 files changed, 1388 insertions, 425 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index a46ab3a9e35b..5235a451d021 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -234,7 +234,7 @@ public class BatteryMeterView extends LinearLayout implements
}
Dependency.get(TunerService.class)
- .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ .addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mIsSubscribedForTunerUpdates = true;
}
@@ -287,8 +287,8 @@ public class BatteryMeterView extends LinearLayout implements
@Override
public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
- ArraySet<String> icons = StatusBarIconController.getIconBlacklist(
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ ArraySet<String> icons = StatusBarIconController.getIconHideList(
getContext(), newValue);
setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 7861211e802d..ad11d71eb132 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -48,10 +48,11 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.graphics.drawable.VectorDrawable;
+import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -61,6 +62,7 @@ import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayCutout.BoundsPosition;
import android.view.DisplayInfo;
@@ -117,12 +119,15 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private DisplayManager.DisplayListener mDisplayListener;
private CameraAvailabilityListener mCameraListener;
+ //TODO: These are piecemeal being updated to Points for now to support non-square rounded
+ // corners. for now it is only supposed when reading the intrinsic size from the drawables with
+ // mIsRoundedCornerMultipleRadius is set
@VisibleForTesting
- protected int mRoundedDefault;
+ protected Point mRoundedDefault = new Point(0, 0);
@VisibleForTesting
- protected int mRoundedDefaultTop;
+ protected Point mRoundedDefaultTop = new Point(0, 0);
@VisibleForTesting
- protected int mRoundedDefaultBottom;
+ protected Point mRoundedDefaultBottom = new Point(0, 0);
@VisibleForTesting
protected View[] mOverlays;
@Nullable
@@ -375,8 +380,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
if (mOverlays[pos] != null) {
return;
}
- mOverlays[pos] = LayoutInflater.from(mContext)
- .inflate(R.layout.rounded_corners, null);
+ mOverlays[pos] = overlayForPosition(pos);
mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this);
((ViewGroup) mOverlays[pos]).addView(mCutoutViews[pos]);
@@ -405,6 +409,23 @@ public class ScreenDecorations extends SystemUI implements Tunable {
new ValidatingPreDrawListener(mOverlays[pos]));
}
+ /**
+ * Allow overrides for top/bottom positions
+ */
+ private View overlayForPosition(@BoundsPosition int pos) {
+ switch (pos) {
+ case BOUNDS_POSITION_TOP:
+ return LayoutInflater.from(mContext)
+ .inflate(R.layout.rounded_corners_top, null);
+ case BOUNDS_POSITION_BOTTOM:
+ return LayoutInflater.from(mContext)
+ .inflate(R.layout.rounded_corners_bottom, null);
+ default:
+ return LayoutInflater.from(mContext)
+ .inflate(R.layout.rounded_corners, null);
+ }
+ }
+
private void updateView(@BoundsPosition int pos) {
if (mOverlays == null || mOverlays[pos] == null) {
return;
@@ -590,27 +611,36 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
private void updateRoundedCornerRadii() {
+ // We should eventually move to just using the intrinsic size of the drawables since
+ // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
+ // upgrading all of the configs to contain (width, height) pairs. Instead assume that a
+ // device configured using the single integer config value is okay with drawing the corners
+ // as a square
final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius);
final int newRoundedDefaultTop = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius_top);
final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius_bottom);
- final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
- || mRoundedDefaultBottom != newRoundedDefaultBottom
- || mRoundedDefaultTop != newRoundedDefaultTop;
- if (roundedCornersChanged) {
+ final boolean changed = mRoundedDefault.x != newRoundedDefault
+ || mRoundedDefaultTop.x != newRoundedDefault
+ || mRoundedDefaultBottom.x != newRoundedDefault;
+
+ if (changed) {
// If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
- // max(width, height) size of drawable/rounded.xml instead of rounded_corner_radius
+ // (width, height) size of drawable/rounded.xml instead of rounded_corner_radius
if (mIsRoundedCornerMultipleRadius) {
- final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
- mRoundedDefault = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
- mRoundedDefaultTop = mRoundedDefaultBottom = mRoundedDefault;
+ Drawable d = mContext.getDrawable(R.drawable.rounded);
+ mRoundedDefault.set(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ d = mContext.getDrawable(R.drawable.rounded_corner_top);
+ mRoundedDefaultTop.set(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ d = mContext.getDrawable(R.drawable.rounded_corner_bottom);
+ mRoundedDefaultBottom.set(d.getIntrinsicWidth(), d.getIntrinsicHeight());
} else {
- mRoundedDefault = newRoundedDefault;
- mRoundedDefaultTop = newRoundedDefaultTop;
- mRoundedDefaultBottom = newRoundedDefaultBottom;
+ mRoundedDefault.set(newRoundedDefault, newRoundedDefault);
+ mRoundedDefaultTop.set(newRoundedDefaultTop, newRoundedDefaultTop);
+ mRoundedDefaultBottom.set(newRoundedDefaultBottom, newRoundedDefaultBottom);
}
onTuningChanged(SIZE, null);
}
@@ -625,7 +655,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
if (shouldShowRoundedCorner(pos)) {
final int gravity = getRoundedCornerGravity(pos, id == R.id.left);
((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity;
- rounded.setRotation(getRoundedCornerRotation(gravity));
+ setRoundedCornerOrientation(rounded, gravity);
rounded.setVisibility(View.VISIBLE);
}
}
@@ -646,23 +676,38 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
}
- private int getRoundedCornerRotation(int gravity) {
+ /**
+ * Configures the rounded corner drawable's view matrix based on the gravity.
+ *
+ * The gravity describes which corner to configure for, and the drawable we are rotating is
+ * assumed to be oriented for the top-left corner of the device regardless of the target corner.
+ * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
+ * y-axis for the top-right and bottom-left corners.
+ */
+ private void setRoundedCornerOrientation(View corner, int gravity) {
+ corner.setRotation(0);
+ corner.setScaleX(1);
+ corner.setScaleY(1);
switch (gravity) {
case Gravity.TOP | Gravity.LEFT:
- return 0;
+ return;
case Gravity.TOP | Gravity.RIGHT:
- return 90;
+ corner.setScaleX(-1); // flip X axis
+ return;
case Gravity.BOTTOM | Gravity.LEFT:
- return 270;
+ corner.setScaleY(-1); // flip Y axis
+ return;
case Gravity.BOTTOM | Gravity.RIGHT:
- return 180;
+ corner.setRotation(180);
+ return;
default:
throw new IllegalArgumentException("Unsupported gravity: " + gravity);
}
}
-
private boolean hasRoundedCorners() {
- return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0
+ return mRoundedDefault.x > 0
+ || mRoundedDefaultBottom.x > 0
+ || mRoundedDefaultTop.x > 0
|| mIsRoundedCornerMultipleRadius;
}
@@ -712,12 +757,13 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mHandler.post(() -> {
if (mOverlays == null) return;
if (SIZE.equals(key)) {
- int size = mRoundedDefault;
- int sizeTop = mRoundedDefaultTop;
- int sizeBottom = mRoundedDefaultBottom;
+ Point size = mRoundedDefault;
+ Point sizeTop = mRoundedDefaultTop;
+ Point sizeBottom = mRoundedDefaultBottom;
if (newValue != null) {
try {
- size = (int) (Integer.parseInt(newValue) * mDensity);
+ int s = (int) (Integer.parseInt(newValue) * mDensity);
+ size = new Point(s, s);
} catch (Exception e) {
}
}
@@ -726,14 +772,17 @@ public class ScreenDecorations extends SystemUI implements Tunable {
});
}
- private void updateRoundedCornerSize(int sizeDefault, int sizeTop, int sizeBottom) {
+ private void updateRoundedCornerSize(
+ Point sizeDefault,
+ Point sizeTop,
+ Point sizeBottom) {
if (mOverlays == null) {
return;
}
- if (sizeTop == 0) {
+ if (sizeTop.x == 0) {
sizeTop = sizeDefault;
}
- if (sizeBottom == 0) {
+ if (sizeBottom.x == 0) {
sizeBottom = sizeDefault;
}
@@ -760,10 +809,10 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
@VisibleForTesting
- protected void setSize(View view, int pixelSize) {
+ protected void setSize(View view, Point pixelSize) {
LayoutParams params = view.getLayoutParams();
- params.width = pixelSize;
- params.height = pixelSize;
+ params.width = pixelSize.x;
+ params.height = pixelSize.y;
view.setLayoutParams(params);
}
@@ -772,6 +821,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f;
+ private Display.Mode mDisplayMode = null;
private final DisplayInfo mInfo = new DisplayInfo();
private final Paint mPaint = new Paint();
private final List<Rect> mBounds = new ArrayList();
@@ -856,11 +906,33 @@ public class ScreenDecorations extends SystemUI implements Tunable {
@Override
public void onDisplayChanged(int displayId) {
+ Display.Mode oldMode = mDisplayMode;
+ mDisplayMode = getDisplay().getMode();
+
+ // Display mode hasn't meaningfully changed, we can ignore it
+ if (!modeChanged(oldMode, mDisplayMode)) {
+ return;
+ }
+
if (displayId == getDisplay().getDisplayId()) {
update();
}
}
+ private boolean modeChanged(Display.Mode oldMode, Display.Mode newMode) {
+ if (oldMode == null) {
+ return true;
+ }
+
+ boolean changed = false;
+ changed |= oldMode.getPhysicalHeight() != newMode.getPhysicalHeight();
+ changed |= oldMode.getPhysicalWidth() != newMode.getPhysicalWidth();
+ // We purposely ignore refresh rate and id changes here, because we don't need to
+ // invalidate for those, and they can trigger the refresh rate to increase
+
+ return changed;
+ }
+
public void setRotation(int rotation) {
mRotation = rotation;
update();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 6f103b020814..e252195da136 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -207,12 +207,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
/** Whether or not the BubbleStackView has been added to the WindowManager. */
private boolean mAddedToWindowManager = false;
- /**
- * Value from {@link NotificationShadeWindowController#getForceHasTopUi()} when we forced top UI
- * due to expansion. We'll restore this value when the stack collapses.
- */
- private boolean mHadTopUi = false;
-
// Listens to user switch so bubbles can be saved and restored.
private final NotificationLockscreenUserManager mNotifUserManager;
@@ -1303,7 +1297,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Collapsing? Do this first before remaining steps.
if (update.expandedChanged && !update.expanded) {
mStackView.setExpanded(false);
- mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
// Do removals, if any.
@@ -1393,8 +1387,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (update.expandedChanged && update.expanded) {
if (mStackView != null) {
mStackView.setExpanded(true);
- mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi();
- mNotificationShadeWindowController.setForceHasTopUi(true);
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index d5b54f94d938..2569f7c107cb 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -54,25 +54,28 @@ public class WirelessChargingAnimation {
* before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
* @hide
*/
- public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
- batteryLevel, Callback callback, boolean isDozing) {
+ public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
+ int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
- batteryLevel, callback, isDozing);
+ transmittingBatteryLevel, batteryLevel, callback, isDozing);
}
/**
* Creates a wireless charging animation object populated with next view.
+ *
* @hide
*/
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
- @Nullable Looper looper, int batteryLevel, Callback callback, boolean isDozing) {
- return new WirelessChargingAnimation(context, looper, batteryLevel, callback, isDozing);
+ @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
+ Callback callback, boolean isDozing) {
+ return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
+ batteryLevel, callback, isDozing);
}
/**
* Show the view for the specified duration.
*/
- public void show() {
+ public void show(long delay) {
if (mCurrentWirelessChargingView == null ||
mCurrentWirelessChargingView.mNextView == null) {
throw new RuntimeException("setView must have been called");
@@ -83,8 +86,8 @@ public class WirelessChargingAnimation {
}
mPreviousWirelessChargingView = mCurrentWirelessChargingView;
- mCurrentWirelessChargingView.show();
- mCurrentWirelessChargingView.hide(DURATION);
+ mCurrentWirelessChargingView.show(delay);
+ mCurrentWirelessChargingView.hide(delay + DURATION);
}
private static class WirelessChargingView {
@@ -100,10 +103,12 @@ public class WirelessChargingAnimation {
private WindowManager mWM;
private Callback mCallback;
- public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel,
- Callback callback, boolean isDozing) {
+ public WirelessChargingView(Context context, @Nullable Looper looper,
+ int transmittingBatteryLevel, int batteryLevel, Callback callback,
+ boolean isDozing) {
mCallback = callback;
- mNextView = new WirelessChargingLayout(context, batteryLevel, isDozing);
+ mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
+ isDozing);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
final WindowManager.LayoutParams params = mParams;
@@ -149,9 +154,9 @@ public class WirelessChargingAnimation {
};
}
- public void show() {
+ public void show(long delay) {
if (DEBUG) Slog.d(TAG, "SHOW: " + this);
- mHandler.obtainMessage(SHOW).sendToTarget();
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, SHOW), delay);
}
public void hide(long duration) {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index ec150873d56e..e8407f01516b 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.Animatable;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
@@ -37,16 +38,17 @@ import java.text.NumberFormat;
* @hide
*/
public class WirelessChargingLayout extends FrameLayout {
- private final static int UNKNOWN_BATTERY_LEVEL = -1;
+ public final static int UNKNOWN_BATTERY_LEVEL = -1;
public WirelessChargingLayout(Context context) {
super(context);
init(context, null, false);
}
- public WirelessChargingLayout(Context context, int batteryLevel, boolean isDozing) {
+ public WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
+ boolean isDozing) {
super(context);
- init(context, null, batteryLevel, isDozing);
+ init(context, null, transmittingBatteryLevel, batteryLevel, isDozing);
}
public WirelessChargingLayout(Context context, AttributeSet attrs) {
@@ -55,11 +57,13 @@ public class WirelessChargingLayout extends FrameLayout {
}
private void init(Context c, AttributeSet attrs, boolean isDozing) {
- init(c, attrs, -1, false);
+ init(c, attrs, -1, -1, false);
}
- private void init(Context context, AttributeSet attrs, int batteryLevel, boolean isDozing) {
- final int mBatteryLevel = batteryLevel;
+ private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
+ int batteryLevel, boolean isDozing) {
+ final boolean showTransmittingBatteryLevel =
+ (transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
// set style based on background
int style = R.style.ChargingAnim_WallpaperBackground;
@@ -74,39 +78,40 @@ public class WirelessChargingLayout extends FrameLayout {
final Animatable chargingAnimation = (Animatable) chargingView.getDrawable();
// amount of battery:
- final TextView mPercentage = findViewById(R.id.wireless_charging_percentage);
+ final TextView percentage = findViewById(R.id.wireless_charging_percentage);
if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
- mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
- mPercentage.setAlpha(0);
+ percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
+ percentage.setAlpha(0);
}
- final long chargingAnimationFadeStartOffset = (long) context.getResources().getInteger(
+ final long chargingAnimationFadeStartOffset = context.getResources().getInteger(
R.integer.wireless_charging_fade_offset);
- final long chargingAnimationFadeDuration = (long) context.getResources().getInteger(
+ final long chargingAnimationFadeDuration = context.getResources().getInteger(
R.integer.wireless_charging_fade_duration);
final float batteryLevelTextSizeStart = context.getResources().getFloat(
R.dimen.wireless_charging_anim_battery_level_text_size_start);
final float batteryLevelTextSizeEnd = context.getResources().getFloat(
- R.dimen.wireless_charging_anim_battery_level_text_size_end);
+ R.dimen.wireless_charging_anim_battery_level_text_size_end) * (
+ showTransmittingBatteryLevel ? 0.75f : 1.0f);
// Animation Scale: battery percentage text scales from 0% to 100%
- ValueAnimator textSizeAnimator = ObjectAnimator.ofFloat(mPercentage, "textSize",
+ ValueAnimator textSizeAnimator = ObjectAnimator.ofFloat(percentage, "textSize",
batteryLevelTextSizeStart, batteryLevelTextSizeEnd);
textSizeAnimator.setInterpolator(new PathInterpolator(0, 0, 0, 1));
- textSizeAnimator.setDuration((long) context.getResources().getInteger(
+ textSizeAnimator.setDuration(context.getResources().getInteger(
R.integer.wireless_charging_battery_level_text_scale_animation_duration));
// Animation Opacity: battery percentage text transitions from 0 to 1 opacity
- ValueAnimator textOpacityAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 0, 1);
+ ValueAnimator textOpacityAnimator = ObjectAnimator.ofFloat(percentage, "alpha", 0, 1);
textOpacityAnimator.setInterpolator(Interpolators.LINEAR);
- textOpacityAnimator.setDuration((long) context.getResources().getInteger(
+ textOpacityAnimator.setDuration(context.getResources().getInteger(
R.integer.wireless_charging_battery_level_text_opacity_duration));
- textOpacityAnimator.setStartDelay((long) context.getResources().getInteger(
+ textOpacityAnimator.setStartDelay(context.getResources().getInteger(
R.integer.wireless_charging_anim_opacity_offset));
// Animation Opacity: battery percentage text fades from 1 to 0 opacity
- ValueAnimator textFadeAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 1, 0);
+ ValueAnimator textFadeAnimator = ObjectAnimator.ofFloat(percentage, "alpha", 1, 0);
textFadeAnimator.setDuration(chargingAnimationFadeDuration);
textFadeAnimator.setInterpolator(Interpolators.LINEAR);
textFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
@@ -114,7 +119,80 @@ public class WirelessChargingLayout extends FrameLayout {
// play all animations together
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
+
+ if (!showTransmittingBatteryLevel) {
+ chargingAnimation.start();
+ animatorSet.start();
+ return;
+ }
+
+ // amount of transmitting battery:
+ final TextView transmittingPercentage = findViewById(
+ R.id.reverse_wireless_charging_percentage);
+ transmittingPercentage.setVisibility(VISIBLE);
+ transmittingPercentage.setText(
+ NumberFormat.getPercentInstance().format(transmittingBatteryLevel / 100f));
+ transmittingPercentage.setAlpha(0);
+
+ // Animation Scale: transmitting battery percentage text scales from 0% to 100%
+ ValueAnimator textSizeAnimatorTransmitting = ObjectAnimator.ofFloat(transmittingPercentage,
+ "textSize", batteryLevelTextSizeStart, batteryLevelTextSizeEnd);
+ textSizeAnimatorTransmitting.setInterpolator(new PathInterpolator(0, 0, 0, 1));
+ textSizeAnimatorTransmitting.setDuration(context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_scale_animation_duration));
+
+ // Animation Opacity: transmitting battery percentage text transitions from 0 to 1 opacity
+ ValueAnimator textOpacityAnimatorTransmitting = ObjectAnimator.ofFloat(
+ transmittingPercentage, "alpha", 0, 1);
+ textOpacityAnimatorTransmitting.setInterpolator(Interpolators.LINEAR);
+ textOpacityAnimatorTransmitting.setDuration(context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_opacity_duration));
+ textOpacityAnimatorTransmitting.setStartDelay(
+ context.getResources().getInteger(R.integer.wireless_charging_anim_opacity_offset));
+
+ // Animation Opacity: transmitting battery percentage text fades from 1 to 0 opacity
+ ValueAnimator textFadeAnimatorTransmitting = ObjectAnimator.ofFloat(transmittingPercentage,
+ "alpha", 1, 0);
+ textFadeAnimatorTransmitting.setDuration(chargingAnimationFadeDuration);
+ textFadeAnimatorTransmitting.setInterpolator(Interpolators.LINEAR);
+ textFadeAnimatorTransmitting.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // play all animations together
+ AnimatorSet animatorSetTransmitting = new AnimatorSet();
+ animatorSetTransmitting.playTogether(textSizeAnimatorTransmitting,
+ textOpacityAnimatorTransmitting, textFadeAnimatorTransmitting);
+
+ // transmitting battery icon
+ final ImageView chargingViewIcon = findViewById(R.id.reverse_wireless_charging_icon);
+ chargingViewIcon.setVisibility(VISIBLE);
+ final int padding = Math.round(
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, batteryLevelTextSizeEnd,
+ getResources().getDisplayMetrics()));
+ chargingViewIcon.setPadding(padding, 0, padding, 0);
+
+ // Animation Opacity: transmitting battery icon transitions from 0 to 1 opacity
+ ValueAnimator textOpacityAnimatorIcon = ObjectAnimator.ofFloat(chargingViewIcon, "alpha", 0,
+ 1);
+ textOpacityAnimatorIcon.setInterpolator(Interpolators.LINEAR);
+ textOpacityAnimatorIcon.setDuration(context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_opacity_duration));
+ textOpacityAnimatorIcon.setStartDelay(
+ context.getResources().getInteger(R.integer.wireless_charging_anim_opacity_offset));
+
+ // Animation Opacity: transmitting battery icon fades from 1 to 0 opacity
+ ValueAnimator textFadeAnimatorIcon = ObjectAnimator.ofFloat(chargingViewIcon, "alpha", 1,
+ 0);
+ textFadeAnimatorIcon.setDuration(chargingAnimationFadeDuration);
+ textFadeAnimatorIcon.setInterpolator(Interpolators.LINEAR);
+ textFadeAnimatorIcon.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // play all animations together
+ AnimatorSet animatorSetIcon = new AnimatorSet();
+ animatorSetIcon.playTogether(textOpacityAnimatorIcon, textFadeAnimatorIcon);
+
chargingAnimation.start();
animatorSet.start();
+ animatorSetTransmitting.start();
+ animatorSetIcon.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index ef2ef4570fca..f35322bd2a77 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.SensorManager;
import android.net.Uri;
import android.provider.DeviceConfig;
-import android.util.DisplayMetrics;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
@@ -62,7 +61,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
private final ProximitySensor mProximitySensor;
- private final DisplayMetrics mDisplayMetrics;
+ private final FalsingDataProvider mFalsingDataProvider;
private FalsingManager mInternalFalsingManager;
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
private final DeviceConfigProxy mDeviceConfig;
@@ -74,20 +73,21 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
- DisplayMetrics displayMetrics, ProximitySensor proximitySensor,
+ ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfig, DockManager dockManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
@UiBackground Executor uiBgExecutor,
- StatusBarStateController statusBarStateController) {
- mDisplayMetrics = displayMetrics;
+ StatusBarStateController statusBarStateController,
+ FalsingDataProvider falsingDataProvider) {
mProximitySensor = proximitySensor;
mDockManager = dockManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mUiBgExecutor = uiBgExecutor;
mStatusBarStateController = statusBarStateController;
+ mFalsingDataProvider = falsingDataProvider;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
- mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
+ mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
@@ -143,7 +143,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
- new FalsingDataProvider(mDisplayMetrics),
+ mFalsingDataProvider,
mKeyguardUpdateMonitor,
mProximitySensor,
mDeviceConfig,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 62254a64dfcc..a50f9ce9713b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -76,7 +77,7 @@ public class BrightLineFalsingManager implements FalsingManager {
private final List<FalsingClassifier> mClassifiers;
- private ProximitySensor.ProximitySensorListener mSensorEventListener = this::onProximityEvent;
+ private ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -131,7 +132,9 @@ public class BrightLineFalsingManager implements FalsingManager {
}
private void registerSensors() {
- mProximitySensor.register(mSensorEventListener);
+ if (!mDataProvider.isWirelessCharging()) {
+ mProximitySensor.register(mSensorEventListener);
+ }
}
private void unregisterSensors() {
@@ -240,7 +243,7 @@ public class BrightLineFalsingManager implements FalsingManager {
mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent));
}
- private void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {
+ private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
// make these calls.
mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index cf088213644e..85e95a66bfe3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -96,7 +96,7 @@ abstract class FalsingClassifier {
/**
* Called when a ProximityEvent occurs (change in near/far).
*/
- void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {};
+ void onProximityEvent(ProximitySensor.ThresholdSensorEvent proximityEvent) {};
/**
* The phone screen has turned on and we need to begin falsing detection.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 5494c644c22c..ea46441c8fbe 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -22,10 +22,13 @@ import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import com.android.systemui.classifier.Classifier;
+import com.android.systemui.statusbar.policy.BatteryController;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Acts as a cache and utility class for FalsingClassifiers.
*/
@@ -36,6 +39,7 @@ public class FalsingDataProvider {
private final int mWidthPixels;
private final int mHeightPixels;
+ private final BatteryController mBatteryController;
private final float mXdpi;
private final float mYdpi;
@@ -50,11 +54,13 @@ public class FalsingDataProvider {
private MotionEvent mFirstRecentMotionEvent;
private MotionEvent mLastMotionEvent;
- public FalsingDataProvider(DisplayMetrics displayMetrics) {
+ @Inject
+ public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) {
mXdpi = displayMetrics.xdpi;
mYdpi = displayMetrics.ydpi;
mWidthPixels = displayMetrics.widthPixels;
mHeightPixels = displayMetrics.heightPixels;
+ mBatteryController = batteryController;
FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels());
@@ -177,6 +183,11 @@ public class FalsingDataProvider {
return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
}
+ /** Returns true if phone is being charged without a cable. */
+ boolean isWirelessCharging() {
+ return mBatteryController.isWirelessCharging();
+ }
+
private void recalculateData() {
if (!mDirty) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
index 749914e1b625..b128678af5db 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -101,8 +101,8 @@ class ProximityClassifier extends FalsingClassifier {
@Override
public void onProximityEvent(
- ProximitySensor.ProximityEvent proximityEvent) {
- boolean near = proximityEvent.getNear();
+ ProximitySensor.ThresholdSensorEvent proximityEvent) {
+ boolean near = proximityEvent.getBelow();
long timestampNs = proximityEvent.getTimestampNs();
logDebug("Sensor is: " + near + " at time " + timestampNs);
update(near, timestampNs);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 5697fc0d577f..f683a639af10 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -37,6 +37,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.content.res.Resources;
+import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
@@ -254,6 +255,12 @@ public class SystemServicesModule {
return context.getResources();
}
+ @Provides
+ @Singleton
+ static SensorManager providesSensorManager(Context context) {
+ return context.getSystemService(SensorManager.class);
+ }
+
@Singleton
@Provides
static SensorPrivacyManager provideSensorPrivacyManager(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 3bb953ab9da3..aeba64a59a7a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -36,6 +36,7 @@ import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
@@ -70,7 +71,7 @@ import dagger.Provides;
* A dagger module for injecting default implementations of components of System UI that may be
* overridden by the System UI implementation.
*/
-@Module(includes = {DividerModule.class})
+@Module(includes = {DividerModule.class, QSModule.class})
public abstract class SystemUIDefaultModule {
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 90cd13fd1330..cb45926d3f24 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
@@ -62,7 +63,8 @@ import dagger.Provides;
ConcurrencyModule.class,
LogModule.class,
PeopleHubModule.class,
- SettingsModule.class
+ SensorModule.class,
+ SettingsModule.class,
},
subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 490890f263aa..ae7d82ac4a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -199,6 +199,12 @@ public class DozeMachine {
requestState(State.DOZE_REQUEST_PULSE, pulseReason);
}
+ void onScreenState(int state) {
+ for (Part part : mParts) {
+ part.onScreenState(state);
+ }
+ }
+
private void requestState(State requestedState, int pulseReason) {
Assert.isMainThread();
if (DEBUG) {
@@ -423,6 +429,9 @@ public class DozeMachine {
/** Give the Part a chance to clean itself up. */
default void destroy() {}
+
+ /** Alerts that the screenstate is being changed. */
+ default void onScreenState(int state) {}
}
/** A wrapper interface for {@link android.service.dreams.DreamService} */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index f6fccc00bf99..64cfb4bcd058 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -29,6 +29,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -68,6 +69,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
* --ei brightness_bucket 1}
*/
private int mDebugBrightnessBucket = -1;
+ private DozeMachine.State mState;
@VisibleForTesting
public DozeScreenBrightness(Context context, DozeMachine.Service service,
@@ -107,17 +109,10 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ mState = newState;
switch (newState) {
case INITIALIZED:
- resetBrightnessToDefault();
- break;
- case DOZE_AOD:
- case DOZE_REQUEST_PULSE:
- case DOZE_AOD_DOCKED:
- setLightSensorEnabled(true);
- break;
case DOZE:
- setLightSensorEnabled(false);
resetBrightnessToDefault();
break;
case FINISH:
@@ -130,6 +125,18 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
}
}
+ @Override
+ public void onScreenState(int state) {
+ if (!mScreenOff
+ && (mState == DozeMachine.State.DOZE_AOD
+ || mState == DozeMachine.State.DOZE_AOD_DOCKED)
+ && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND)) {
+ setLightSensorEnabled(true);
+ } else {
+ setLightSensorEnabled(false);
+ }
+ }
+
private void onDestroy() {
setLightSensorEnabled(false);
if (mDebuggable) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 78f8f673cab9..aebf41b884c4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -37,6 +37,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.view.Display;
import androidx.annotation.VisibleForTesting;
@@ -66,7 +67,6 @@ public class DozeSensors {
private final AlarmManager mAlarmManager;
private final AsyncSensorManager mSensorManager;
private final ContentResolver mResolver;
- private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
@@ -80,7 +80,6 @@ public class DozeSensors {
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
- private boolean mPaused;
@VisibleForTesting
public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum {
@@ -122,7 +121,7 @@ public class DozeSensors {
dozeParameters.getPulseOnSigMotion(),
DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
false /* touchscreen */, dozeLog),
- mPickupSensor = new TriggerSensor(
+ new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PICK_UP_GESTURE,
true /* settingDef */,
@@ -179,7 +178,7 @@ public class DozeSensors {
mProximitySensor.register(
proximityEvent -> {
if (proximityEvent != null) {
- mProxCallback.accept(!proximityEvent.getNear());
+ mProxCallback.accept(!proximityEvent.getBelow());
}
});
}
@@ -232,18 +231,6 @@ public class DozeSensors {
}
/**
- * Unregister sensors, when listening, unless they are prox gated.
- * @see #setListening(boolean)
- */
- public void setPaused(boolean paused) {
- if (mPaused == paused) {
- return;
- }
- mPaused = paused;
- updateListening();
- }
-
- /**
* Registers/unregisters sensors based on internal state.
*/
public void updateListening() {
@@ -280,6 +267,13 @@ public class DozeSensors {
}
}
+ void onScreenState(int state) {
+ mProximitySensor.setSecondarySafe(
+ state == Display.STATE_DOZE
+ || state == Display.STATE_DOZE_SUSPEND
+ || state == Display.STATE_OFF);
+ }
+
public void setProxListening(boolean listen) {
if (mProximitySensor.isRegistered() && listen) {
mProximitySensor.alertListeners();
@@ -304,10 +298,6 @@ public class DozeSensors {
}
};
- public void setDisableSensorsInterferingWithProximity(boolean disable) {
- mPickupSensor.setDisabled(disable);
- }
-
/** Ignore the setting value of only the sensors that require the touchscreen. */
public void ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore) {
for (TriggerSensor sensor : mSensors) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 529b016aaca6..d2bebb7b27d1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -130,4 +130,10 @@ public class DozeService extends DreamService
mDozeMachine.requestState(DozeMachine.State.DOZE);
}
}
+
+ @Override
+ public void setDozeScreenState(int state) {
+ super.setDozeScreenState(state);
+ mDozeMachine.onScreenState(state);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 82639ba4375e..cbf8f578744c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -30,6 +30,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.Formatter;
import android.util.Log;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -92,6 +93,9 @@ public class DozeTriggers implements DozeMachine.Part {
private boolean mPulsePending;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private boolean mWantProx;
+ private boolean mWantSensors;
+ private boolean mWantTouchScreenSensors;
@VisibleForTesting
public enum DozingUpdateUiEvent implements UiEventLogger.UiEventEnum {
@@ -382,24 +386,25 @@ public class DozeTriggers implements DozeMachine.Part {
break;
case DOZE:
case DOZE_AOD:
- mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
- mDozeSensors.setListening(true);
- mDozeSensors.setPaused(false);
+ mWantProx = newState != DozeMachine.State.DOZE;
+ mWantSensors = true;
+ mWantTouchScreenSensors = true;
if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
onWakeScreen(false, newState);
}
break;
case DOZE_AOD_PAUSED:
case DOZE_AOD_PAUSING:
- mDozeSensors.setProxListening(true);
- mDozeSensors.setPaused(true);
+ mWantProx = true;
break;
case DOZE_PULSING:
case DOZE_PULSING_BRIGHT:
+ mWantProx = true;
+ mWantTouchScreenSensors = false;
+ break;
case DOZE_AOD_DOCKED:
- mDozeSensors.setTouchscreenSensorsListening(false);
- mDozeSensors.setProxListening(true);
- mDozeSensors.setPaused(false);
+ mWantProx = false;
+ mWantTouchScreenSensors = false;
break;
case DOZE_PULSE_DONE:
mDozeSensors.requestTemporaryDisable();
@@ -413,11 +418,28 @@ public class DozeTriggers implements DozeMachine.Part {
mDockManager.removeListener(mDockEventListener);
mDozeSensors.setListening(false);
mDozeSensors.setProxListening(false);
+ mWantSensors = false;
+ mWantProx = false;
+ mWantTouchScreenSensors = false;
break;
default:
}
}
+ @Override
+ public void onScreenState(int state) {
+ mDozeSensors.onScreenState(state);
+ if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
+ || state == Display.STATE_OFF) {
+ mDozeSensors.setProxListening(mWantProx);
+ mDozeSensors.setListening(mWantSensors);
+ mDozeSensors.setTouchscreenSensorsListening(mWantTouchScreenSensors);
+ } else {
+ mDozeSensors.setProxListening(false);
+ mDozeSensors.setListening(mWantSensors);
+ }
+ }
+
private void checkTriggersAtInit() {
if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
|| mDozeHost.isBlockingDoze()
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b2e91643bed2..ef51abb1404d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2129,7 +2129,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mShowing;
private float mScrimAlpha;
private ResetOrientationData mResetOrientationData;
- private boolean mHadTopUi;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationShadeDepthController mDepthController;
private final SysUiState mSysUiState;
@@ -2397,8 +2396,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
public void show() {
super.show();
mShowing = true;
- mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi();
- mNotificationShadeWindowController.setForceHasTopUi(true);
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
@@ -2499,7 +2497,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
dismissOverflow(true);
dismissPowerOptions(true);
if (mControlsUiController != null) mControlsUiController.hide();
- mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index 0a84f5ee1bb9..38b20ee45946 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -57,12 +57,18 @@ public class AutoAddTracker implements UserAwareController {
mContext = context;
mUserId = userId;
mAutoAdded = new ArraySet<>(getAdded());
+ }
+
+ /**
+ * Init method must be called after construction to start listening
+ */
+ public void initialize() {
// TODO: remove migration code and shared preferences keys after P release
if (mUserId == UserHandle.USER_SYSTEM) {
for (String[] convertPref : CONVERT_PREFS) {
- if (Prefs.getBoolean(context, convertPref[0], false)) {
+ if (Prefs.getBoolean(mContext, convertPref[0], false)) {
setTileAdded(convertPref[1]);
- Prefs.remove(context, convertPref[0]);
+ Prefs.remove(mContext, convertPref[0]);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
new file mode 100644
index 000000000000..8740581240b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 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.qs.dagger;
+
+import android.content.Context;
+import android.hardware.display.NightDisplayListener;
+import android.os.Handler;
+
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.qs.AutoAddTracker;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.phone.AutoTileManager;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.HotspotController;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module for QS dependencies
+ */
+// TODO: Add other QS classes
+@Module
+public interface QSModule {
+
+ @Provides
+ static AutoTileManager provideAutoTileManager(
+ Context context,
+ AutoAddTracker.Builder autoAddTrackerBuilder,
+ QSTileHost host,
+ @Background Handler handler,
+ HotspotController hotspotController,
+ DataSaverController dataSaverController,
+ ManagedProfileController managedProfileController,
+ NightDisplayListener nightDisplayListener,
+ CastController castController) {
+ AutoTileManager manager = new AutoTileManager(context, autoAddTrackerBuilder,
+ host, handler, hotspotController, dataSaverController, managedProfileController,
+ nightDisplayListener, castController);
+ manager.init();
+ return manager;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index fbcd6ba0ff47..42dde4064a97 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -205,7 +205,8 @@ public class ScreenshotNotificationsController {
mPublicNotificationBuilder
.setContentTitle(mResources.getString(R.string.screenshot_saved_title))
.setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
+ .setContentIntent(PendingIntent
+ .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
.setWhen(now)
.setAutoCancel(true)
.setColor(mContext.getColor(
@@ -213,7 +214,8 @@ public class ScreenshotNotificationsController {
mNotificationBuilder
.setContentTitle(mResources.getString(R.string.screenshot_saved_title))
.setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
+ .setContentIntent(PendingIntent
+ .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
.setWhen(now)
.setAutoCancel(true)
.setColor(mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 9abc66056452..d04389d6cbe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,6 +36,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -189,6 +190,8 @@ public class NotificationEntryManager implements
}
}
+ private final Lazy<BubbleController> mBubbleControllerLazy;
+
/**
* Injected constructor. See {@link NotificationsModule}.
*/
@@ -201,6 +204,7 @@ public class NotificationEntryManager implements
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
+ Lazy<BubbleController> bubbleController,
ForegroundServiceDismissalFeatureController fgsFeatureController) {
mLogger = logger;
mGroupManager = groupManager;
@@ -211,6 +215,7 @@ public class NotificationEntryManager implements
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
+ mBubbleControllerLazy = bubbleController;
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -920,8 +925,20 @@ public class NotificationEntryManager implements
/**
* @return {@code true} if there is at least one notification that should be visible right now
*/
- public boolean hasActiveNotifications() {
- return mReadOnlyNotifications.size() != 0;
+ public boolean hasVisibleNotifications() {
+ if (mReadOnlyNotifications.size() == 0) {
+ return false;
+ }
+
+ // Filter out suppressed notifications, which are active notifications backing a bubble
+ // but are not present in the shade
+ for (NotificationEntry e : mSortedAndFiltered) {
+ if (!mBubbleControllerLazy.get().isBubbleNotificationSuppressedFromShade(e)) {
+ return true;
+ }
+ }
+
+ return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index df1de63b65a0..c37e93d4fcc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -87,6 +87,7 @@ public interface NotificationsModule {
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
+ Lazy<BubbleController> bubbleController,
ForegroundServiceDismissalFeatureController fgsFeatureController) {
return new NotificationEntryManager(
logger,
@@ -97,6 +98,7 @@ public interface NotificationsModule {
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
leakDetector,
+ bubbleController,
fgsFeatureController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b9d31a93f408..a4a58194a46b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -6580,7 +6580,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
return !mNotifPipeline.getShadeList().isEmpty();
} else {
- return mEntryManager.hasActiveNotifications();
+ return mEntryManager.hasVisibleNotifications();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 825919f17661..db9956a4074f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -40,8 +40,6 @@ import com.android.systemui.util.UserAwareController;
import java.util.ArrayList;
import java.util.Objects;
-import javax.inject.Inject;
-
/**
* Manages which tiles should be automatically added to QS.
*/
@@ -57,6 +55,7 @@ public class AutoTileManager implements UserAwareController {
static final String SETTING_SEPARATOR = ":";
private UserHandle mCurrentUser;
+ private boolean mInitialized;
private final Context mContext;
private final QSTileHost mHost;
@@ -69,7 +68,6 @@ public class AutoTileManager implements UserAwareController {
private final CastController mCastController;
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
- @Inject
public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
QSTileHost host,
@Background Handler handler,
@@ -88,9 +86,20 @@ public class AutoTileManager implements UserAwareController {
mManagedProfileController = managedProfileController;
mNightDisplayListener = nightDisplayListener;
mCastController = castController;
+ }
+ /**
+ * Init method must be called after construction to start listening
+ */
+ public void init() {
+ if (mInitialized) {
+ Log.w(TAG, "Trying to re-initialize");
+ return;
+ }
+ mAutoTracker.initialize();
populateSettingsList();
startControllersAndSettingsListeners();
+ mInitialized = true;
}
protected void startControllersAndSettingsListeners() {
@@ -168,8 +177,14 @@ public class AutoTileManager implements UserAwareController {
}
}
+ /*
+ * This will be sent off the main thread if needed
+ */
@Override
public void changeUser(UserHandle newUser) {
+ if (!mInitialized) {
+ throw new IllegalStateException("AutoTileManager not initialized");
+ }
if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
mHandler.post(() -> changeUser(newUser));
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 303a0831b52f..0e76c904f8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -152,6 +152,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private final Context mContext;
private final int mWakeUpDelay;
private int mMode;
+ private BiometricSourceType mBiometricType;
private KeyguardViewController mKeyguardViewController;
private DozeScrimController mDozeScrimController;
private KeyguardViewMediator mKeyguardViewMediator;
@@ -340,6 +341,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
Trace.endSection();
return;
}
+ mBiometricType = biometricSourceType;
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.SUCCESS_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
@@ -615,6 +617,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private void resetMode() {
mMode = MODE_NONE;
+ mBiometricType = null;
mNotificationShadeWindowController.setForceDozeBrightness(false);
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
@@ -680,8 +683,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
/**
* Successful authentication with fingerprint, face, or iris when the lockscreen fades away
*/
- public boolean isUnlockFading() {
- return mMode == MODE_UNLOCK_FADING;
+ public BiometricSourceType getBiometricType() {
+ return mBiometricType;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 8e192c5bf17d..c758670fc457 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -97,7 +97,7 @@ public class LightsOutNotifController {
}
private boolean hasActiveNotifications() {
- return mEntryManager.hasActiveNotifications();
+ return mEntryManager.hasVisibleNotifications();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 5d3910b4c415..7a8dc32741bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -83,6 +83,7 @@ public class LockscreenLockIconController {
private boolean mWakeAndUnlockRunning;
private boolean mShowingLaunchAffordance;
private boolean mBouncerShowingScrimmed;
+ private boolean mFingerprintUnlock;
private int mStatusBarState = StatusBarState.SHADE;
private LockIcon mLockIcon;
@@ -389,14 +390,19 @@ public class LockscreenLockIconController {
/**
* We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
* icon on top of the black front scrim.
+ * We also want to halt padlock the animation when we're in face bypass mode or dismissing the
+ * keyguard with fingerprint.
* @param wakeAndUnlock are we wake and unlocking
* @param isUnlock are we currently unlocking
*/
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock,
+ BiometricSourceType type) {
if (wakeAndUnlock) {
mWakeAndUnlockRunning = true;
}
- if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) {
+ mFingerprintUnlock = type == BiometricSourceType.FINGERPRINT;
+ if (isUnlock && (mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled())
+ && canBlockUpdates()) {
// We don't want the icon to change while we are unlocking
mBlockUpdates = true;
}
@@ -513,10 +519,13 @@ public class LockscreenLockIconController {
&& (!mStatusBarStateController.isPulsing() || mDocked);
boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
|| mShowingLaunchAffordance;
- if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
+ boolean fingerprintOrBypass = mFingerprintUnlock
+ || mKeyguardBypassController.getBypassEnabled();
+ if (fingerprintOrBypass && !mBouncerShowingScrimmed) {
if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
|| mHeadsUpManagerPhone.hasPinnedHeadsUp()
- || mStatusBarState == StatusBarState.KEYGUARD)
+ || mStatusBarState == StatusBarState.KEYGUARD
+ || mStatusBarState == StatusBarState.SHADE)
&& !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
invisible = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 375af6b099c2..64202d221b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3003,7 +3003,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateShowEmptyShadeView() {
boolean
showEmptyShadeView =
- mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
+ mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasVisibleNotifications();
showEmptyShadeView(showEmptyShadeView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 5164440c1463..bc73be19ab59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -61,6 +61,8 @@ import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -432,7 +434,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
}
private void applyHasTopUi(State state) {
- mHasTopUiChanged = state.mForceHasTopUi || isExpanded(state);
+ mHasTopUiChanged = !state.mComponentsForcingTopUi.isEmpty() || isExpanded(state);
}
private void applyNotTouchable(State state) {
@@ -635,12 +637,17 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
apply(mCurrentState);
}
- public boolean getForceHasTopUi() {
- return mCurrentState.mForceHasTopUi;
- }
-
- public void setForceHasTopUi(boolean forceHasTopUi) {
- mCurrentState.mForceHasTopUi = forceHasTopUi;
+ /**
+ * SystemUI may need top-ui to avoid jank when performing animations. After the
+ * animation is performed, the component should remove itself from the list of features that
+ * are forcing SystemUI to be top-ui.
+ */
+ public void setRequestTopUi(boolean requestTopUi, String componentTag) {
+ if (requestTopUi) {
+ mCurrentState.mComponentsForcingTopUi.add(componentTag);
+ } else {
+ mCurrentState.mComponentsForcingTopUi.remove(componentTag);
+ }
apply(mCurrentState);
}
@@ -663,7 +670,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
boolean mBackdropShowing;
boolean mWallpaperSupportsAmbientMode;
boolean mNotTouchable;
- boolean mForceHasTopUi;
+ Set<String> mComponentsForcingTopUi = new HashSet<>();
/**
* The {@link StatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 60fc17d9474a..686b87127239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -348,10 +348,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
- // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
- // with too many things at this case, in order to not skip the initial frames.
- mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ scheduleUpdate();
} else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
|| (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
// Scheduling a frame isn't enough when:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a5cc1a83fc85..f125b7d10035 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -29,7 +29,10 @@ import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -45,6 +48,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -114,6 +118,10 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.MetricsLogger;
@@ -205,7 +213,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -237,7 +244,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ActivityStarter, KeyguardStateController.Callback,
OnHeadsUpChangedListener, CommandQueue.Callbacks,
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
- StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback {
+ StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback,
+ LifecycleOwner, BatteryController.BatteryStateChangeCallback {
public static final boolean MULTIUSER_DEBUG = false;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -586,7 +594,8 @@ public class StatusBar extends SystemUI implements DemoMode,
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private final UserSwitcherController mUserSwitcherController;
private final NetworkController mNetworkController;
- private final BatteryController mBatteryController;
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+ protected final BatteryController mBatteryController;
protected boolean mPanelExpanded;
private UiModeManager mUiModeManager;
protected boolean mIsKeyguard;
@@ -935,6 +944,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mConfigurationController.addCallback(this);
+ mBatteryController.observe(mLifecycle, this);
+ mLifecycle.setCurrentState(RESUMED);
+
// set the initial view visibility
int disabledFlags1 = result.mDisabledFlags1;
int disabledFlags2 = result.mDisabledFlags2;
@@ -1101,22 +1113,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById(
R.id.ambient_indication_container);
- // TODO: Find better place for this callback.
- mBatteryController.addCallback(new BatteryStateChangeCallback() {
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
- mHandler.post(mCheckBarModes);
- if (mDozeServiceHost != null) {
- mDozeServiceHost.firePowerSaveChanged(isPowerSave);
- }
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- // noop
- }
- });
-
mAutoHideController.setStatusBar(new AutoHideUiElement() {
@Override
public void synchronizeState() {
@@ -1267,6 +1263,25 @@ public class StatusBar extends SystemUI implements DemoMode,
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
}
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ mHandler.post(mCheckBarModes);
+ if (mDozeServiceHost != null) {
+ mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+ }
+ }
+
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ // noop
+ }
+
@VisibleForTesting
protected void registerBroadcastReceiver() {
IntentFilter filter = new IntentFilter();
@@ -2369,24 +2384,43 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void showWirelessChargingAnimation(int batteryLevel) {
+ showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
+ }
+
+ protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
+ long animationDelay) {
if (mDozing || mKeyguardManager.isKeyguardLocked()) {
// on ambient or lockscreen, hide notification panel
WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- batteryLevel, new WirelessChargingAnimation.Callback() {
+ transmittingBatteryLevel, batteryLevel,
+ new WirelessChargingAnimation.Callback() {
@Override
public void onAnimationStarting() {
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
}
@Override
public void onAnimationEnded() {
CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
- }, mDozing).show();
+ }, mDozing).show(animationDelay);
} else {
// workspace
WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- batteryLevel, null, false).show();
+ transmittingBatteryLevel, batteryLevel,
+ new WirelessChargingAnimation.Callback() {
+ @Override
+ public void onAnimationStarting() {
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
+ }
+
+ @Override
+ public void onAnimationEnded() {
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
+ }
+ }, false).show(animationDelay);
}
}
@@ -3947,7 +3981,8 @@ public class StatusBar extends SystemUI implements DemoMode,
updateScrimController();
mLockscreenLockIconController.onBiometricAuthModeChanged(
mBiometricUnlockController.isWakeAndUnlock(),
- mBiometricUnlockController.isBiometricUnlock());
+ mBiometricUnlockController.isBiometricUnlock(),
+ mBiometricUnlockController.getBiometricType());
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 93df14f18fda..0364186a83a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -83,15 +83,16 @@ public interface StatusBarIconController {
public void removeIcon(String slot, int tag);
public void removeAllIconsForSlot(String slot);
- public static final String ICON_BLACKLIST = "icon_blacklist";
+ // TODO: See if we can rename this tunable name.
+ String ICON_HIDE_LIST = "icon_blacklist";
- /** Reads the default blacklist from config value unless blacklistStr is provided. */
- static ArraySet<String> getIconBlacklist(Context context, String blackListStr) {
+ /** Reads the default hide list from config value unless hideListStr is provided. */
+ static ArraySet<String> getIconHideList(Context context, String hideListStr) {
ArraySet<String> ret = new ArraySet<>();
- String[] blacklist = blackListStr == null
+ String[] hideList = hideListStr == null
? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
- : blackListStr.split(",");
- for (String slot : blacklist) {
+ : hideListStr.split(",");
+ for (String slot : hideList) {
if (!TextUtils.isEmpty(slot)) {
ret.add(slot);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index d0e806769f14..21e1d319cffa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -59,7 +59,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
private static final String TAG = "StatusBarIconController";
private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
- private final ArraySet<String> mIconBlacklist = new ArraySet<>();
+ private final ArraySet<String> mIconHideList = new ArraySet<>();
// Points to light or dark context depending on the... context?
private Context mContext;
@@ -79,7 +79,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
loadDimens();
commandQueue.addCallback(this);
- Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, ICON_HIDE_LIST);
}
@Override
@@ -89,12 +89,12 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
for (int i = 0; i < allSlots.size(); i++) {
Slot slot = allSlots.get(i);
List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
- boolean blocked = mIconBlacklist.contains(slot.getName());
+ boolean hidden = mIconHideList.contains(slot.getName());
for (StatusBarIconHolder holder : holders) {
int tag = holder.getTag();
int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
- group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
+ group.onIconAdded(viewIndex, slot.getName(), hidden, holder);
}
}
}
@@ -107,11 +107,11 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
@Override
public void onTuningChanged(String key, String newValue) {
- if (!ICON_BLACKLIST.equals(key)) {
+ if (!ICON_HIDE_LIST.equals(key)) {
return;
}
- mIconBlacklist.clear();
- mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(mContext, newValue));
+ mIconHideList.clear();
+ mIconHideList.addAll(StatusBarIconController.getIconHideList(mContext, newValue));
ArrayList<Slot> currentSlots = getSlots();
ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
@@ -142,9 +142,9 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
private void addSystemIcon(int index, StatusBarIconHolder holder) {
String slot = getSlotName(index);
int viewIndex = getViewIndex(index, holder.getTag());
- boolean blocked = mIconBlacklist.contains(slot);
+ boolean hidden = mIconHideList.contains(slot);
- mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
+ mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 45f0c49a4fd4..8e933a281b3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -330,7 +330,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
}
public boolean hasActiveNotifications() {
- return mEntryManager.hasActiveNotifications();
+ return mEntryManager.hasVisibleNotifications();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 690d57345db6..7eefaf28517e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -52,12 +52,12 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
private final SecurityController mSecurityController;
private final Handler mHandler = Handler.getMain();
- private boolean mBlockAirplane;
- private boolean mBlockMobile;
- private boolean mBlockWifi;
- private boolean mBlockEthernet;
+ private boolean mHideAirplane;
+ private boolean mHideMobile;
+ private boolean mHideWifi;
+ private boolean mHideEthernet;
private boolean mActivityEnabled;
- private boolean mForceBlockWifi;
+ private boolean mForceHideWifi;
// Track as little state as possible, and only for padding purposes
private boolean mIsAirplaneMode = false;
@@ -80,7 +80,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
}
@@ -114,21 +114,21 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
@Override
public void onTuningChanged(String key, String newValue) {
- if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
return;
}
- ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(mContext, newValue);
- boolean blockAirplane = blockList.contains(mSlotAirplane);
- boolean blockMobile = blockList.contains(mSlotMobile);
- boolean blockWifi = blockList.contains(mSlotWifi);
- boolean blockEthernet = blockList.contains(mSlotEthernet);
-
- if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
- || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
- mBlockAirplane = blockAirplane;
- mBlockMobile = blockMobile;
- mBlockEthernet = blockEthernet;
- mBlockWifi = blockWifi || mForceBlockWifi;
+ ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue);
+ boolean hideAirplane = hideList.contains(mSlotAirplane);
+ boolean hideMobile = hideList.contains(mSlotMobile);
+ boolean hideWifi = hideList.contains(mSlotWifi);
+ boolean hideEthernet = hideList.contains(mSlotEthernet);
+
+ if (hideAirplane != mHideAirplane || hideMobile != mHideMobile
+ || hideEthernet != mHideEthernet || hideWifi != mHideWifi) {
+ mHideAirplane = hideAirplane;
+ mHideMobile = hideMobile;
+ mHideEthernet = hideEthernet;
+ mHideWifi = hideWifi || mForceHideWifi;
// Re-register to get new callbacks.
mNetworkController.removeCallback(this);
mNetworkController.addCallback(this);
@@ -140,7 +140,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
boolean activityIn, boolean activityOut, String description, boolean isTransient,
String statusLabel) {
- boolean visible = statusIcon.visible && !mBlockWifi;
+ boolean visible = statusIcon.visible && !mHideWifi;
boolean in = activityIn && mActivityEnabled && visible;
boolean out = activityOut && mActivityEnabled && visible;
@@ -189,7 +189,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
// Visibility of the data type indicator changed
boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
- state.visible = statusIcon.visible && !mBlockMobile;
+ state.visible = statusIcon.visible && !mHideMobile;
state.strengthId = statusIcon.icon;
state.typeId = statusType;
state.contentDescription = statusIcon.contentDescription;
@@ -270,7 +270,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
@Override
public void setEthernetIndicators(IconState state) {
- boolean visible = state.visible && !mBlockEthernet;
+ boolean visible = state.visible && !mHideEthernet;
int resId = state.icon;
String description = state.contentDescription;
@@ -284,7 +284,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
@Override
public void setIsAirplaneMode(IconState icon) {
- mIsAirplaneMode = icon.visible && !mBlockAirplane;
+ mIsAirplaneMode = icon.visible && !mHideAirplane;
int resId = icon.icon;
String description = icon.contentDescription;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index b9168e3c2223..673549ab589f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -58,6 +58,11 @@ public interface BatteryController extends DemoMode, Dumpable,
default void init() { }
/**
+ * Returns {@code true} if the device is currently in wireless charging mode.
+ */
+ default boolean isWirelessCharging() { return false; }
+
+ /**
* Returns {@code true} if reverse is supported.
*/
default boolean isReverseSupported() { return false; }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index c4c0d3fdff42..d43dd232e7c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -190,7 +190,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter,
Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL);
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
- StatusBarIconController.ICON_BLACKLIST);
+ StatusBarIconController.ICON_HIDE_LIST);
mCommandQueue.addCallback(this);
if (mShowDark) {
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
@@ -296,8 +296,8 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
if (CLOCK_SECONDS.equals(key)) {
mShowSeconds = TunerService.parseIntegerSwitch(newValue, false);
updateShowSeconds();
- } else {
- setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(getContext(), newValue)
+ } else if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ setClockVisibleByUser(!StatusBarIconController.getIconHideList(getContext(), newValue)
.contains("clock"));
updateClockVisibility();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 66372c311325..b71aafdf6b96 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -38,7 +38,7 @@ public class BatteryPreference extends DropDownPreference implements TunerServic
private final String mBattery;
private boolean mBatteryEnabled;
private boolean mHasPercentage;
- private ArraySet<String> mBlacklist;
+ private ArraySet<String> mHideList;
private boolean mHasSetValue;
public BatteryPreference(Context context, AttributeSet attrs) {
@@ -52,7 +52,7 @@ public class BatteryPreference extends DropDownPreference implements TunerServic
super.onAttached();
mHasPercentage = Settings.System.getInt(getContext().getContentResolver(),
SHOW_BATTERY_PERCENT, 0) != 0;
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
}
@Override
@@ -63,9 +63,9 @@ public class BatteryPreference extends DropDownPreference implements TunerServic
@Override
public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
- mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
- mBatteryEnabled = !mBlacklist.contains(mBattery);
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ mHideList = StatusBarIconController.getIconHideList(getContext(), newValue);
+ mBatteryEnabled = !mHideList.contains(mBattery);
}
if (!mHasSetValue) {
// Because of the complicated tri-state it can end up looping and setting state back to
@@ -88,12 +88,12 @@ public class BatteryPreference extends DropDownPreference implements TunerServic
MetricsLogger.action(getContext(), MetricsEvent.TUNER_BATTERY_PERCENTAGE, v);
Settings.System.putInt(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, v ? 1 : 0);
if (DISABLED.equals(value)) {
- mBlacklist.add(mBattery);
+ mHideList.add(mBattery);
} else {
- mBlacklist.remove(mBattery);
+ mHideList.remove(mBattery);
}
- Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", mBlacklist));
+ Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", mHideList));
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
index f7d0c9fb9d86..c92d7bbfe0b7 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
@@ -33,7 +33,7 @@ public class ClockPreference extends DropDownPreference implements TunerService.
private final String mClock;
private boolean mClockEnabled;
private boolean mHasSeconds;
- private ArraySet<String> mBlacklist;
+ private ArraySet<String> mHideList;
private boolean mHasSetValue;
private boolean mReceivedSeconds;
private boolean mReceivedClock;
@@ -47,7 +47,7 @@ public class ClockPreference extends DropDownPreference implements TunerService.
@Override
public void onAttached() {
super.onAttached();
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST,
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST,
Clock.CLOCK_SECONDS);
}
@@ -59,10 +59,10 @@ public class ClockPreference extends DropDownPreference implements TunerService.
@Override
public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
mReceivedClock = true;
- mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
- mClockEnabled = !mBlacklist.contains(mClock);
+ mHideList = StatusBarIconController.getIconHideList(getContext(), newValue);
+ mClockEnabled = !mHideList.contains(mClock);
} else if (Clock.CLOCK_SECONDS.equals(key)) {
mReceivedSeconds = true;
mHasSeconds = newValue != null && Integer.parseInt(newValue) != 0;
@@ -87,12 +87,12 @@ public class ClockPreference extends DropDownPreference implements TunerService.
Dependency.get(TunerService.class).setValue(Clock.CLOCK_SECONDS, SECONDS.equals(value) ? 1
: 0);
if (DISABLED.equals(value)) {
- mBlacklist.add(mClock);
+ mHideList.add(mClock);
} else {
- mBlacklist.remove(mClock);
+ mHideList.remove(mClock);
}
- Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", mBlacklist));
+ Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", mHideList));
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
index de8ccfa848e3..cc0050b64d60 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
@@ -34,7 +34,7 @@ import java.util.Set;
public class StatusBarSwitch extends SwitchPreference implements Tunable {
- private Set<String> mBlacklist;
+ private Set<String> mHideList;
public StatusBarSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -43,7 +43,7 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable {
@Override
public void onAttached() {
super.onAttached();
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
}
@Override
@@ -54,35 +54,35 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable {
@Override
public void onTuningChanged(String key, String newValue) {
- if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
return;
}
- mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
- setChecked(!mBlacklist.contains(getKey()));
+ mHideList = StatusBarIconController.getIconHideList(getContext(), newValue);
+ setChecked(!mHideList.contains(getKey()));
}
@Override
protected boolean persistBoolean(boolean value) {
if (!value) {
- // If not enabled add to blacklist.
- if (!mBlacklist.contains(getKey())) {
+ // If not enabled add to hideList.
+ if (!mHideList.contains(getKey())) {
MetricsLogger.action(getContext(), MetricsEvent.TUNER_STATUS_BAR_DISABLE,
getKey());
- mBlacklist.add(getKey());
- setList(mBlacklist);
+ mHideList.add(getKey());
+ setList(mHideList);
}
} else {
- if (mBlacklist.remove(getKey())) {
+ if (mHideList.remove(getKey())) {
MetricsLogger.action(getContext(), MetricsEvent.TUNER_STATUS_BAR_ENABLE, getKey());
- setList(mBlacklist);
+ setList(mHideList);
}
}
return true;
}
- private void setList(Set<String> blacklist) {
+ private void setList(Set<String> hideList) {
ContentResolver contentResolver = getContext().getContentResolver();
- Settings.Secure.putStringForUser(contentResolver, StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", blacklist), ActivityManager.getCurrentUser());
+ Settings.Secure.putStringForUser(contentResolver, StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", hideList), ActivityManager.getCurrentUser());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 9ad2aa257aa0..644f7582f146 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -60,7 +60,7 @@ public class TunerServiceImpl extends TunerService {
// Things that use the tunable infrastructure but are now real user settings and
// shouldn't be reset with tuner settings.
- private static final String[] RESET_BLACKLIST = new String[] {
+ private static final String[] RESET_EXCEPTION_LIST = new String[] {
QSTileHost.TILES_SETTING,
Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.MEDIA_CONTROLS_RESUME
@@ -116,17 +116,17 @@ public class TunerServiceImpl extends TunerService {
private void upgradeTuner(int oldVersion, int newVersion, Handler mainHandler) {
if (oldVersion < 1) {
- String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
- if (blacklistStr != null) {
- ArraySet<String> iconBlacklist =
- StatusBarIconController.getIconBlacklist(mContext, blacklistStr);
+ String hideListStr = getValue(StatusBarIconController.ICON_HIDE_LIST);
+ if (hideListStr != null) {
+ ArraySet<String> iconHideList =
+ StatusBarIconController.getIconHideList(mContext, hideListStr);
- iconBlacklist.add("rotate");
- iconBlacklist.add("headset");
+ iconHideList.add("rotate");
+ iconHideList.add("headset");
Settings.Secure.putStringForUser(mContentResolver,
- StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", iconBlacklist), mCurrentUser);
+ StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", iconHideList), mCurrentUser);
}
}
if (oldVersion < 2) {
@@ -251,7 +251,7 @@ public class TunerServiceImpl extends TunerService {
mContext.sendBroadcast(intent);
for (String key : mTunableLookup.keySet()) {
- if (ArrayUtils.contains(RESET_BLACKLIST, key)) {
+ if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key)) {
continue;
}
Settings.Secure.putStringForUser(mContentResolver, key, null, user);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7561af770298..b1241b160d70 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@ public class UsbDebuggingActivity extends AlertActivity
if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
}
Intent intent = getIntent();
@@ -119,6 +121,7 @@ public class UsbDebuggingActivity extends AlertActivity
}
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
if (!connected) {
+ Log.d(TAG, "USB disconnected, notifying service");
notifyService(false);
mActivity.finish();
}
@@ -126,29 +129,20 @@ public class UsbDebuggingActivity extends AlertActivity
}
@Override
- public void onStart() {
- super.onStart();
- if (mDisconnectedReceiver != null) {
- IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
- mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
- }
- }
-
- @Override
- protected void onStop() {
+ protected void onDestroy() {
if (mDisconnectedReceiver != null) {
mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
}
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- // If the ADB service has not yet been notified due to this dialog being closed in some
- // other way then notify the service to deny the connection to ensure system_server sends
- // a response to adbd.
- if (!mServiceNotified) {
- notifyService(false);
+ // Only notify the service if the activity is finishing; if onDestroy has been called due to
+ // a configuration change then allow the user to still authorize the connection the next
+ // time the activity is in the foreground.
+ if (isFinishing()) {
+ // If the ADB service has not yet been notified due to this dialog being closed in some
+ // other way then notify the service to deny the connection to ensure system_server
+ // sends a response to adbd.
+ if (!mServiceNotified) {
+ notifyService(false);
+ }
}
super.onDestroy();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
index b25df5f9c07f..5e7280840bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java
@@ -104,9 +104,13 @@ public class LeakReporter {
.setContentText(String.format(
"SystemUI has detected %d leaked objects. Tap to send", garbageCount))
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
+ .setContentIntent(PendingIntent.getActivityAsUser(
+ mContext,
+ 0,
getIntent(hprofFile, dumpFile),
- PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.CURRENT));
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ null,
+ UserHandle.CURRENT));
notiMan.notify(TAG, 0, builder.build());
} catch (IOException e) {
Log.e(TAG, "Couldn't dump heap for leak", e);
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java
new file mode 100644
index 000000000000..96c76c1a15d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.util.sensors;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface PrimaryProxSensor {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 52d46476df83..06806d0e6ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -16,94 +16,128 @@
package com.android.systemui.util.sensors;
-import android.content.res.Resources;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import javax.inject.Inject;
/**
- * Simple wrapper around SensorManager customized for the Proximity sensor.
+ * Wrapper around SensorManager customized for the Proximity sensor.
+ *
+ * The ProximitySensor supports the concept of a primary and a
+ * secondary hardware sensor. The primary sensor is used for a first
+ * pass check if the phone covered. When triggered, it then checks
+ * the secondary sensor for confirmation (if there is one). It does
+ * not send a proximity event until the secondary sensor confirms (or
+ * rejects) the reading. The secondary sensor is, in fact, the source
+ * of truth.
+ *
+ * This is necessary as sometimes keeping the secondary sensor on for
+ * extends periods is undesirable. It may, however, result in increased
+ * latency for proximity readings.
+ *
+ * Phones should configure this via a config.xml overlay. If no
+ * proximity sensor is set (primary or secondary) we fall back to the
+ * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in
+ * config.xml, that will be used as the primary sensor. If
+ * proximity_sensor_secondary_type is set, that will function as the
+ * secondary sensor. If no secondary is set, only the primary will be
+ * used.
*/
-public class ProximitySensor {
+public class ProximitySensor implements ThresholdSensor {
private static final String TAG = "ProxSensor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final long SECONDARY_PING_INTERVAL_MS = 5000;
- private final Sensor mSensor;
- private final AsyncSensorManager mSensorManager;
- private final float mThreshold;
- private List<ProximitySensorListener> mListeners = new ArrayList<>();
+ private final ThresholdSensor mPrimaryThresholdSensor;
+ private final ThresholdSensor mSecondaryThresholdSensor;
+ private final DelayableExecutor mDelayableExecutor;
+ private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
private String mTag = null;
- @VisibleForTesting ProximityEvent mLastEvent;
- private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
@VisibleForTesting protected boolean mPaused;
+ private ThresholdSensorEvent mLastPrimaryEvent;
+ @VisibleForTesting
+ ThresholdSensorEvent mLastEvent;
private boolean mRegistered;
private final AtomicBoolean mAlerting = new AtomicBoolean();
+ private Runnable mCancelSecondaryRunnable;
+ private boolean mInitializedListeners = false;
+ private boolean mSecondarySafe = false;
- private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ private ThresholdSensor.Listener mPrimaryEventListener = new ThresholdSensor.Listener() {
@Override
- public synchronized void onSensorChanged(SensorEvent event) {
- onSensorEvent(event);
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
+ onPrimarySensorEvent(event);
}
};
- @Inject
- public ProximitySensor(@Main Resources resources,
- AsyncSensorManager sensorManager) {
- mSensorManager = sensorManager;
-
- Sensor sensor = findCustomProxSensor(resources);
- float threshold = 0;
- if (sensor != null) {
- try {
- threshold = getCustomProxThreshold(resources);
- } catch (IllegalStateException e) {
- Log.e(TAG, "Can not load custom proximity sensor.", e);
- sensor = null;
+ private ThresholdSensor.Listener mSecondaryEventListener = new ThresholdSensor.Listener() {
+ @Override
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
+ // If we no longer have a "below" signal and the secondary sensor is not
+ // considered "safe", then we need to turn it off.
+ if (!mSecondarySafe
+ && (mLastPrimaryEvent == null
+ || !mLastPrimaryEvent.getBelow()
+ || !event.getBelow())) {
+ mSecondaryThresholdSensor.pause();
+ if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
+ // Only check the secondary as long as the primary thinks we're near.
+ mCancelSecondaryRunnable = null;
+ return;
+ } else {
+ // Check this sensor again in a moment.
+ mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(
+ mSecondaryThresholdSensor::resume, SECONDARY_PING_INTERVAL_MS);
+ }
}
- }
- if (sensor == null) {
- sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- if (sensor != null) {
- threshold = sensor.getMaximumRange();
+ logDebug("Secondary sensor event: " + event.getBelow() + ".");
+
+ if (!mPaused) {
+ onSensorEvent(event);
}
}
+ };
- mThreshold = threshold;
-
- mSensor = sensor;
+ @Inject
+ public ProximitySensor(@PrimaryProxSensor ThresholdSensor primary,
+ @SecondaryProxSensor ThresholdSensor secondary,
+ @Main DelayableExecutor delayableExecutor) {
+ mPrimaryThresholdSensor = primary;
+ mSecondaryThresholdSensor = secondary;
+ mDelayableExecutor = delayableExecutor;
}
+ @Override
public void setTag(String tag) {
mTag = tag;
+ mPrimaryThresholdSensor.setTag(tag + ":primary");
+ mSecondaryThresholdSensor.setTag(tag + ":secondary");
}
- public void setSensorDelay(int sensorDelay) {
- mSensorDelay = sensorDelay;
+ @Override
+ public void setDelay(int delay) {
+ Assert.isMainThread();
+ mPrimaryThresholdSensor.setDelay(delay);
+ mSecondaryThresholdSensor.setDelay(delay);
}
/**
* Unregister with the {@link SensorManager} without unsetting listeners on this object.
*/
+ @Override
public void pause() {
+ Assert.isMainThread();
mPaused = true;
unregisterInternal();
}
@@ -111,39 +145,20 @@ public class ProximitySensor {
/**
* Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
*/
+ @Override
public void resume() {
+ Assert.isMainThread();
mPaused = false;
registerInternal();
}
- /**
- * Returns a brightness sensor that can be used for proximity purposes.
- */
- private Sensor findCustomProxSensor(Resources resources) {
- String sensorType = resources.getString(R.string.proximity_sensor_type);
- if (sensorType.isEmpty()) {
- return null;
- }
-
- List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- Sensor sensor = null;
- for (Sensor s : sensorList) {
- if (sensorType.equals(s.getStringType())) {
- sensor = s;
- break;
- }
- }
-
- return sensor;
- }
/**
- * Returns a threshold value that can be used along with {@link #findCustomProxSensor}
+ * Sets that it is safe to leave the secondary sensor on indefinitely.
*/
- private float getCustomProxThreshold(Resources resources) {
- try {
- return resources.getFloat(R.dimen.proximity_sensor_threshold);
- } catch (Resources.NotFoundException e) {
- throw new IllegalStateException("R.dimen.proximity_sensor_threshold must be set.");
+ public void setSecondarySafe(boolean safe) {
+ mSecondarySafe = safe;
+ if (!mSecondarySafe) {
+ mSecondaryThresholdSensor.pause();
}
}
@@ -157,38 +172,46 @@ public class ProximitySensor {
/**
* Returns {@code false} if a Proximity sensor is not available.
*/
- public boolean getSensorAvailable() {
- return mSensor != null;
+ @Override
+ public boolean isLoaded() {
+ return mPrimaryThresholdSensor.isLoaded();
}
/**
* Add a listener.
*
* Registers itself with the {@link SensorManager} if this is the first listener
- * added. If a cool down is currently running, the sensor will be registered when it is over.
+ * added. If the ProximitySensor is paused, it will be registered when resumed.
*/
- public boolean register(ProximitySensorListener listener) {
- if (!getSensorAvailable()) {
- return false;
+ @Override
+ public void register(ThresholdSensor.Listener listener) {
+ Assert.isMainThread();
+ if (!isLoaded()) {
+ return;
}
if (mListeners.contains(listener)) {
- Log.d(TAG, "ProxListener registered multiple times: " + listener);
+ logDebug("ProxListener registered multiple times: " + listener);
} else {
mListeners.add(listener);
}
registerInternal();
-
- return true;
}
protected void registerInternal() {
+ Assert.isMainThread();
if (mRegistered || mPaused || mListeners.isEmpty()) {
return;
}
+ if (!mInitializedListeners) {
+ mPrimaryThresholdSensor.register(mPrimaryEventListener);
+ mSecondaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.register(mSecondaryEventListener);
+ mInitializedListeners = true;
+ }
logDebug("Registering sensor listener");
+ mPrimaryThresholdSensor.resume();
mRegistered = true;
- mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
}
/**
@@ -197,7 +220,9 @@ public class ProximitySensor {
* If all listeners are removed from an instance of this class,
* it will unregister itself with the SensorManager.
*/
- public void unregister(ProximitySensorListener listener) {
+ @Override
+ public void unregister(ThresholdSensor.Listener listener) {
+ Assert.isMainThread();
mListeners.remove(listener);
if (mListeners.size() == 0) {
unregisterInternal();
@@ -205,40 +230,85 @@ public class ProximitySensor {
}
protected void unregisterInternal() {
+ Assert.isMainThread();
if (!mRegistered) {
return;
}
logDebug("unregistering sensor listener");
- mSensorManager.unregisterListener(mSensorEventListener);
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.pause();
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ mCancelSecondaryRunnable = null;
+ }
+ mLastPrimaryEvent = null; // Forget what we know.
+ mLastEvent = null;
mRegistered = false;
}
public Boolean isNear() {
- return getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
+ return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null;
}
/** Update all listeners with the last value this class received from the sensor. */
public void alertListeners() {
+ Assert.isMainThread();
if (mAlerting.getAndSet(true)) {
return;
}
+ if (mLastEvent != null) {
+ ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent.
+ List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
+ listeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onThresholdCrossed(lastEvent));
+ }
- List<ProximitySensorListener> listeners = new ArrayList<>(mListeners);
- listeners.forEach(proximitySensorListener ->
- proximitySensorListener.onSensorEvent(mLastEvent));
mAlerting.set(false);
}
- private void onSensorEvent(SensorEvent event) {
- boolean near = event.values[0] < mThreshold;
- mLastEvent = new ProximityEvent(near, event.timestamp);
+ private void onPrimarySensorEvent(ThresholdSensorEvent event) {
+ Assert.isMainThread();
+ if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
+ return;
+ }
+
+ mLastPrimaryEvent = event;
+
+ if (event.getBelow() && mSecondaryThresholdSensor.isLoaded()) {
+ logDebug("Primary sensor is near. Checking secondary.");
+ if (mCancelSecondaryRunnable == null) {
+ mSecondaryThresholdSensor.resume();
+ }
+ } else {
+ if (!mSecondaryThresholdSensor.isLoaded()) {
+ logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
+ } else {
+ logDebug("Primary sensor event: " + event.getBelow() + ".");
+ }
+ onSensorEvent(event);
+ }
+ }
+
+ private void onSensorEvent(ThresholdSensorEvent event) {
+ Assert.isMainThread();
+ if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) {
+ return;
+ }
+
+ if (!mSecondarySafe && !event.getBelow()) {
+ mSecondaryThresholdSensor.pause();
+ }
+
+ mLastEvent = event;
alertListeners();
}
@Override
public String toString() {
- return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}",
- isRegistered(), mPaused, isNear(), mSensor);
+ return String.format("{registered=%s, paused=%s, near=%s, primarySensor=%s, "
+ + "secondarySensor=%s}",
+ isRegistered(), mPaused, isNear(), mPrimaryThresholdSensor,
+ mSecondaryThresholdSensor);
}
/**
@@ -249,7 +319,7 @@ public class ProximitySensor {
private final ProximitySensor mSensor;
private final DelayableExecutor mDelayableExecutor;
private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
- private final ProximitySensor.ProximitySensorListener mListener;
+ private final ThresholdSensor.Listener mListener;
private final AtomicBoolean mRegistered = new AtomicBoolean();
@Inject
@@ -268,14 +338,14 @@ public class ProximitySensor {
@Override
public void run() {
unregister();
- mSensor.alertListeners();
+ onProximityEvent(null);
}
/**
* Query the proximity sensor, timing out if no result.
*/
public void check(long timeoutMs, Consumer<Boolean> callback) {
- if (!mSensor.getSensorAvailable()) {
+ if (!mSensor.isLoaded()) {
callback.accept(null);
}
mCallbacks.add(callback);
@@ -290,54 +360,17 @@ public class ProximitySensor {
mRegistered.set(false);
}
- private void onProximityEvent(ProximityEvent proximityEvent) {
+ private void onProximityEvent(ThresholdSensorEvent proximityEvent) {
mCallbacks.forEach(
booleanConsumer ->
booleanConsumer.accept(
- proximityEvent == null ? null : proximityEvent.getNear()));
+ proximityEvent == null ? null : proximityEvent.getBelow()));
mCallbacks.clear();
unregister();
mRegistered.set(false);
}
}
- /** Implement to be notified of ProximityEvents. */
- public interface ProximitySensorListener {
- /** Called when the ProximitySensor changes. */
- void onSensorEvent(ProximityEvent proximityEvent);
- }
-
- /**
- * Returned when the near/far state of a {@link ProximitySensor} changes.
- */
- public static class ProximityEvent {
- private final boolean mNear;
- private final long mTimestampNs;
-
- public ProximityEvent(boolean near, long timestampNs) {
- mNear = near;
- mTimestampNs = timestampNs;
- }
-
- public boolean getNear() {
- return mNear;
- }
-
- public long getTimestampNs() {
- return mTimestampNs;
- }
-
- public long getTimestampMs() {
- return mTimestampNs / 1000000;
- }
-
- @Override
- public String toString() {
- return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mNear, mTimestampNs);
- }
-
- }
-
private void logDebug(String msg) {
if (DEBUG) {
Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java
new file mode 100644
index 000000000000..89fc0eabf607
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.util.sensors;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface SecondaryProxSensor {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
new file mode 100644
index 000000000000..7f3756244629
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.util.sensors;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+
+import com.android.systemui.R;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for Sensor related classes.
+ */
+@Module
+public class SensorModule {
+ @Provides
+ @PrimaryProxSensor
+ static ThresholdSensor providePrimaryProxSensor(SensorManager sensorManager,
+ ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ try {
+ return thresholdSensorBuilder
+ .setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL)
+ .setSensorResourceId(R.string.proximity_sensor_type)
+ .setThresholdResourceId(R.dimen.proximity_sensor_threshold)
+ .setThresholdLatchResourceId(R.dimen.proximity_sensor_threshold_latch)
+ .build();
+ } catch (IllegalStateException e) {
+ Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ return thresholdSensorBuilder
+ .setSensor(defaultSensor)
+ .setThresholdValue(defaultSensor != null ? defaultSensor.getMaximumRange() : 0)
+ .build();
+ }
+ }
+
+ @Provides
+ @SecondaryProxSensor
+ static ThresholdSensor provideSecondaryProxSensor(
+ ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ try {
+ return thresholdSensorBuilder
+ .setSensorResourceId(R.string.proximity_sensor_secondary_type)
+ .setThresholdResourceId(R.dimen.proximity_sensor_secondary_threshold)
+ .setThresholdLatchResourceId(R.dimen.proximity_sensor_secondary_threshold_latch)
+ .build();
+ } catch (IllegalStateException e) {
+ return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
new file mode 100644
index 000000000000..363a734a6ae5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 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.util.sensors;
+
+import java.util.Locale;
+
+/**
+ * A wrapper class for sensors that have a boolean state - above/below.
+ */
+public interface ThresholdSensor {
+ /**
+ * Optional label to use for logging.
+ *
+ * This should be set to something meaningful by owner of the instance.
+ */
+ void setTag(String tag);
+
+ /**
+ * Change the delay used when registering the sensor.
+ *
+ * If the sensor is already registered, this should cause it to re-register with the new
+ * delay.
+ */
+ void setDelay(int delay);
+
+ /**
+ * True if this sensor successfully loads and can be listened to.
+ */
+ boolean isLoaded();
+
+ /**
+ * Registers with the sensor and calls the supplied callback on value change.
+ *
+ * If this instance is paused, the listener will be recorded, but no registration with
+ * the underlying physical sensor will occur until {@link #resume()} is called.
+ *
+ * @see #unregister(Listener)
+ */
+ void register(Listener listener);
+
+ /**
+ * Unregisters from the physical sensor without removing any supplied listeners.
+ *
+ * No events will be sent to listeners as long as this sensor is paused.
+ *
+ * @see #resume()
+ * @see #unregister(Listener)
+ */
+ void pause();
+
+ /**
+ * Resumes listening to the physical sensor after previously pausing.
+ *
+ * @see #pause()
+ */
+ void resume();
+
+ /**
+ * Unregister a listener with the sensor.
+ *
+ * @see #register(Listener)
+ */
+ void unregister(Listener listener);
+
+ /**
+ * Interface for listening to events on {@link ThresholdSensor}
+ */
+ interface Listener {
+ /**
+ * Called whenever the threshold for the registered sensor is crossed.
+ */
+ void onThresholdCrossed(ThresholdSensorEvent event);
+ }
+
+ /**
+ * Returned when the below/above state of a {@link ThresholdSensor} changes.
+ */
+ class ThresholdSensorEvent {
+ private final boolean mBelow;
+ private final long mTimestampNs;
+
+ public ThresholdSensorEvent(boolean below, long timestampNs) {
+ mBelow = below;
+ mTimestampNs = timestampNs;
+ }
+
+ public boolean getBelow() {
+ return mBelow;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ public long getTimestampMs() {
+ return mTimestampNs / 1000000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
new file mode 100644
index 000000000000..aa50292edbf7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2020 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.util.sensors;
+
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+class ThresholdSensorImpl implements ThresholdSensor {
+ private static final String TAG = "ThresholdSensor";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final AsyncSensorManager mSensorManager;
+ private final Sensor mSensor;
+ private final float mThreshold;
+ private boolean mRegistered;
+ private boolean mPaused;
+ private List<Listener> mListeners = new ArrayList<>();
+ private Boolean mLastBelow;
+ private String mTag;
+ private final float mThresholdLatch;
+ private int mSensorDelay;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ boolean below = event.values[0] < mThreshold;
+ boolean above = event.values[0] >= mThresholdLatch;
+ logDebug("Sensor value: " + event.values[0]);
+ onSensorEvent(below, above, event.timestamp);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ private ThresholdSensorImpl(AsyncSensorManager sensorManager,
+ Sensor sensor, float threshold, float thresholdLatch, int sensorDelay) {
+ mSensorManager = sensorManager;
+ mSensor = sensor;
+ mThreshold = threshold;
+ mThresholdLatch = thresholdLatch;
+ mSensorDelay = sensorDelay;
+ }
+
+ @Override
+ public void setTag(String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public void setDelay(int delay) {
+ if (delay == mSensorDelay) {
+ return;
+ }
+
+ mSensorDelay = delay;
+ if (isLoaded()) {
+ unregisterInternal();
+ registerInternal();
+ }
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return mSensor != null;
+ }
+
+ @VisibleForTesting
+ boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * Registers the listener with the sensor.
+ *
+ * Multiple listeners are not supported at this time.
+ *
+ * Returns true if the listener was successfully registered. False otherwise.
+ */
+ @Override
+ public void register(Listener listener) {
+ Assert.isMainThread();
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ registerInternal();
+ }
+
+ @Override
+ public void unregister(Listener listener) {
+ Assert.isMainThread();
+ mListeners.remove(listener);
+ unregisterInternal();
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ @Override
+ public void pause() {
+ Assert.isMainThread();
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ @Override
+ public void resume() {
+ Assert.isMainThread();
+ mPaused = false;
+ registerInternal();
+ }
+
+ private void alertListenersInternal(boolean below, long timestampNs) {
+ List<Listener> listeners = new ArrayList<>(mListeners);
+ listeners.forEach(listener ->
+ listener.onThresholdCrossed(new ThresholdSensorEvent(below, timestampNs)));
+ }
+
+ private void registerInternal() {
+ Assert.isMainThread();
+ if (mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ logDebug("Registering sensor listener");
+ mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
+ mRegistered = true;
+ }
+
+ private void unregisterInternal() {
+ Assert.isMainThread();
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("Unregister sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mRegistered = false;
+ mLastBelow = null; // Forget what we know.
+ }
+
+ /**
+ * Call when the sensor reports a new value.
+ *
+ * Separate below-threshold and above-thresholds are specified. this allows latching behavior,
+ * where a different threshold can be specified for triggering the sensor depending on if it's
+ * going from above to below or below to above. To outside listeners of this class, the class
+ * still appears entirely binary.
+ */
+ private void onSensorEvent(boolean belowThreshold, boolean aboveThreshold, long timestampNs) {
+ Assert.isMainThread();
+ if (!mRegistered) {
+ return;
+ }
+ if (mLastBelow != null) {
+ // If we last reported below and are not yet above, change nothing.
+ if (mLastBelow && !aboveThreshold) {
+ return;
+ }
+ // If we last reported above and are not yet below, change nothing.
+ if (!mLastBelow && !belowThreshold) {
+ return;
+ }
+ }
+ mLastBelow = belowThreshold;
+ logDebug("Alerting below: " + belowThreshold);
+ alertListenersInternal(belowThreshold, timestampNs);
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, threshold=%s, sensor=%s}",
+ isLoaded(), mRegistered, mPaused, mThreshold, mSensor);
+ }
+
+ private void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+
+ static class Builder {
+ private final Resources mResources;
+ private final AsyncSensorManager mSensorManager;
+ private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;;
+ private float mThresholdValue;
+ private float mThresholdLatchValue;
+ private Sensor mSensor;
+ private boolean mSensorSet;
+ private boolean mThresholdSet;
+ private boolean mThresholdLatchValueSet;
+
+ @Inject
+ Builder(@Main Resources resources, AsyncSensorManager sensorManager) {
+ mResources = resources;
+ mSensorManager = sensorManager;
+ }
+
+
+ Builder setSensorDelay(int sensorDelay) {
+ mSensorDelay = sensorDelay;
+ return this;
+ }
+
+ Builder setSensorResourceId(int sensorResourceId) {
+ setSensorType(mResources.getString(sensorResourceId));
+ return this;
+ }
+
+ Builder setThresholdResourceId(int thresholdResourceId) {
+ try {
+ setThresholdValue(mResources.getFloat(thresholdResourceId));
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ return this;
+ }
+
+ Builder setThresholdLatchResourceId(int thresholdLatchResourceId) {
+ try {
+ setThresholdLatchValue(mResources.getFloat(thresholdLatchResourceId));
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ return this;
+ }
+
+ Builder setSensorType(String sensorType) {
+ Sensor sensor = findSensorByType(sensorType);
+ if (sensor != null) {
+ setSensor(sensor);
+ }
+ return this;
+ }
+
+ Builder setThresholdValue(float thresholdValue) {
+ mThresholdValue = thresholdValue;
+ mThresholdSet = true;
+ if (!mThresholdLatchValueSet) {
+ mThresholdLatchValue = mThresholdValue;
+ }
+ return this;
+ }
+
+ Builder setThresholdLatchValue(float thresholdLatchValue) {
+ mThresholdLatchValue = thresholdLatchValue;
+ mThresholdLatchValueSet = true;
+ return this;
+ }
+
+ Builder setSensor(Sensor sensor) {
+ mSensor = sensor;
+ mSensorSet = true;
+ return this;
+ }
+
+ /**
+ * Creates a {@link ThresholdSensor} backed by a {@link ThresholdSensorImpl}.
+ */
+ public ThresholdSensor build() {
+ if (!mSensorSet) {
+ throw new IllegalStateException("A sensor was not successfully set.");
+ }
+
+ if (!mThresholdSet) {
+ throw new IllegalStateException("A threshold was not successfully set.");
+ }
+
+ if (mThresholdValue > mThresholdLatchValue) {
+ throw new IllegalStateException(
+ "Threshold must be less than or equal to Threshold Latch");
+ }
+
+ return new ThresholdSensorImpl(
+ mSensorManager, mSensor, mThresholdValue, mThresholdLatchValue, mSensorDelay);
+ }
+
+ private Sensor findSensorByType(String sensorType) {
+ if (sensorType.isEmpty()) {
+ return null;
+ }
+
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ Sensor sensor = null;
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+
+ return sensor;
+ }
+ }
+}