diff options
author | Xin Li <delphij@google.com> | 2020-10-09 11:14:30 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2020-10-10 20:38:57 -0700 |
commit | 986e6eeddf02959975e63a7cd138e70784540bf6 (patch) | |
tree | bf060846893c657436f9429650f2240f048dd0be /packages/SystemUI/src | |
parent | 1015bae311220fe8242c33e58e11a932ed6f8a3a (diff) | |
parent | 539d92beb7ed3638107c6d90c2f6a2e8a891256d (diff) |
Merge ab/6749736 in stage.
Bug: 167233921
Merged-In: Iecef31f7bf10ad97b7e0075cf302ae94e248474a
Change-Id: I9e9c873bc2e05a0cfe3af0bf74725500f3f108db
Diffstat (limited to 'packages/SystemUI/src')
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; + } + } +} |