Merge changes I44ba0f9a,I70afa777 into sc-dev
* changes:
Fix the live caption tooltip, which was sad and broken.
Clip caption container to outline so the ripple doesn't extend past the circular bounds.
diff --git a/boot/Android.bp b/boot/Android.bp
index 3caede4..e8d88a5 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -48,13 +48,57 @@
// bootclasspath.
fragments: [
{
+ apex: "com.android.appsearch",
+ module: "com.android.appsearch-bootclasspath-fragment",
+ },
+ {
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
{
+ apex: "com.android.conscrypt",
+ module: "com.android.conscrypt-bootclasspath-fragment",
+ },
+ {
apex: "com.android.i18n",
module: "i18n-bootclasspath-fragment",
},
+ {
+ apex: "com.android.ipsec",
+ module: "com.android.ipsec-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.media",
+ module: "com.android.media-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.mediaprovider",
+ module: "com.android.mediaprovider-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.os.statsd",
+ module: "com.android.os.statsd-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.permission",
+ module: "com.android.permission-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.scheduling",
+ module: "com.android.scheduling-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.sdkext",
+ module: "com.android.sdkext-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.tethering",
+ module: "com.android.tethering-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.wifi",
+ module: "com.android.wifi-bootclasspath-fragment",
+ },
],
// Additional information needed by hidden api processing.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f0d410f..881e0cf 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -481,6 +481,7 @@
boolean mFullscreenApplied;
boolean mIsFullscreen;
+ private boolean mLastWasInFullscreenMode;
@UnsupportedAppUsage
View mExtractView;
boolean mExtractViewHidden;
@@ -920,8 +921,17 @@
if (mHandler == null) {
mHandler = new Handler(getMainLooper());
}
- mImeSurfaceScheduledForRemoval = true;
- mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS);
+
+ if (mLastWasInFullscreenMode) {
+ // Caching surface / delaying surface removal can cause mServedView to detach in certain
+ // cases in RecyclerView (b/187772544).
+ // TODO(b/188818557): Re-enable IME surface caching for fullscreen mode once detaching
+ // view issues is resolved in RecyclerView.
+ removeImeSurface();
+ } else {
+ mImeSurfaceScheduledForRemoval = true;
+ mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS);
+ }
}
private void removeImeSurface() {
@@ -2350,6 +2360,7 @@
onWindowHidden();
mDecorViewWasVisible = false;
}
+ mLastWasInFullscreenMode = mIsFullscreen;
updateFullscreenMode();
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 370052d..f2857ce 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -368,27 +368,19 @@
};
/** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */
- public byte[] getStatsProto(long sessionEndTimestampMs) {
-
- final long sessionStartMillis = getStatsStartTimestamp();
- // TODO(b/187223764): Use the getStatsEndTimestamp() instead, once that is added.
- final long sessionEndMillis = sessionEndTimestampMs;
- final long sessionDurationMillis = sessionEndTimestampMs - getStatsStartTimestamp();
-
+ public byte[] getStatsProto() {
final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- final int sessionDischargePercentage = getDischargePercentage();
-
final ProtoOutputStream proto = new ProtoOutputStream();
- proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, sessionStartMillis);
- proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, sessionEndMillis);
- proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, sessionDurationMillis);
+ proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
+ proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp());
+ proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration());
deviceBatteryConsumer.writeStatsProto(proto,
BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
writeUidBatteryConsumersProto(proto);
proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE,
- sessionDischargePercentage);
+ getDischargePercentage());
return proto.getBytes();
}
@@ -399,8 +391,8 @@
private void writeUidBatteryConsumersProto(ProtoOutputStream proto) {
final List<UidBatteryConsumer> consumers = getUidBatteryConsumers();
- // TODO: Sort the list by power consumption. If during the for, proto.getRawSize() > 45kb,
- // truncate the remainder of the list.
+ // TODO(b/189225426): Sort the list by power consumption. If during the for,
+ // proto.getRawSize() > 45kb, truncate the remainder of the list.
final int size = consumers.size();
for (int i = 0; i < size; i++) {
final UidBatteryConsumer consumer = consumers.get(i);
diff --git a/core/java/android/os/HidlMemory.java b/core/java/android/os/HidlMemory.java
index 26fc6f0..2539a6b 100644
--- a/core/java/android/os/HidlMemory.java
+++ b/core/java/android/os/HidlMemory.java
@@ -79,6 +79,7 @@
public void close() throws IOException {
if (mHandle != null) {
mHandle.close();
+ mHandle = null;
}
}
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index 01cece3..c82a516 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -94,6 +94,8 @@
* VibrationEffect VibrationEffects} to be played on one or more vibrators.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param effect a combination of effects to be performed by one or more vibrators.
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
@@ -109,6 +111,9 @@
* VibrationEffect} to be played on one or more vibrators.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param effect a combination of effects to be performed by one or more vibrators.
* @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
* specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index bee0a0b..333eebb 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -48,9 +48,8 @@
@Test
public void testGetStatsProto() {
- final long sessionEndTimestampMs = 1050;
final BatteryUsageStats bus = buildBatteryUsageStats();
- final byte[] bytes = bus.getStatsProto(sessionEndTimestampMs);
+ final byte[] bytes = bus.getStatsProto();
BatteryUsageStatsAtomsProto proto;
try {
proto = BatteryUsageStatsAtomsProto.parseFrom(bytes);
@@ -60,9 +59,9 @@
}
assertEquals(bus.getStatsStartTimestamp(), proto.sessionStartMillis);
- assertEquals(sessionEndTimestampMs, proto.sessionEndMillis);
+ assertEquals(bus.getStatsEndTimestamp(), proto.sessionEndMillis);
assertEquals(
- sessionEndTimestampMs - bus.getStatsStartTimestamp(),
+ bus.getStatsEndTimestamp() - bus.getStatsStartTimestamp(),
proto.sessionDurationMillis);
assertEquals(bus.getDischargePercentage(), proto.sessionDischargePercentage);
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 87acfd0..52132e8 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -22,6 +22,7 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
+ android:forceHasOverlappingRendering="false"
android:theme="@style/MediaPlayer">
<com.android.systemui.media.MediaScrollView
android:id="@+id/media_carousel_scroller"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c16f13e..09d4685 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -129,7 +129,21 @@
android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
android:text="@string/report_rejected_touch"
android:visibility="gone" />
-
+ <com.android.systemui.statusbar.phone.TapAgainView
+ android:id="@+id/shade_falsing_tap_again"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ systemui:layout_constraintLeft_toLeftOf="parent"
+ systemui:layout_constraintRight_toRightOf="parent"
+ systemui:layout_constraintBottom_toBottomOf="parent"
+ android:layout_marginBottom="20dp"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp"
+ android:elevation="10dp"
+ android:visibility="gone"
+ />
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
<FrameLayout
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b2ab5f7..5c1e935 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1418,10 +1418,6 @@
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
- <!-- Delay after which the media will start transitioning to the full shade on
- the lockscreen -->
- <dimen name="lockscreen_shade_media_transition_start_delay">40dp</dimen>
-
<!-- Distance that the full shade transition takes in order for qs to fully transition to the
shade -->
<dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
@@ -1430,13 +1426,16 @@
the shade (in alpha) -->
<dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
- <!-- Extra inset for the notifications when accounting for media during the lockscreen to
- shade transition to compensate for the disappearing media -->
- <dimen name="lockscreen_shade_transition_extra_media_inset">-48dp</dimen>
+ <!-- Distance that the full shade transition takes in order for media to fully transition to
+ the shade -->
+ <dimen name="lockscreen_shade_media_transition_distance">140dp</dimen>
<!-- Maximum overshoot for the topPadding of notifications when transitioning to the full
shade -->
- <dimen name="lockscreen_shade_max_top_overshoot">32dp</dimen>
+ <dimen name="lockscreen_shade_notification_movement">24dp</dimen>
+
+ <!-- Maximum overshoot for the pulse expansion -->
+ <dimen name="pulse_expansion_max_top_overshoot">16dp</dimen>
<dimen name="people_space_widget_radius">28dp</dimen>
<dimen name="people_space_image_radius">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index ee69e27..dba530e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -40,6 +40,7 @@
private boolean mIsFalseRobustTap;
private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
+ private final List<FalsingTapListener> mTapListeners = new ArrayList<>();
@Override
public void onSuccessfulUnlock() {
@@ -148,11 +149,15 @@
@Override
public void addTapListener(FalsingTapListener falsingTapListener) {
-
+ mTapListeners.add(falsingTapListener);
}
@Override
public void removeTapListener(FalsingTapListener falsingTapListener) {
+ mTapListeners.remove(falsingTapListener);
+ }
+ public List<FalsingTapListener> getTapListeners() {
+ return mTapListeners;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 73dfe5e..075bc70 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -26,21 +26,23 @@
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
+import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
/**
* Similarly to isShown but also excludes views that have 0 alpha
@@ -80,6 +82,7 @@
wakefulnessLifecycle: WakefulnessLifecycle,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
) {
+
/**
* The root overlay of the hierarchy. This is where the media notification is attached to
* whenever the view is transitioning from one host to another. It also make sure that the
@@ -90,6 +93,30 @@
private var rootView: View? = null
private var currentBounds = Rect()
private var animationStartBounds: Rect = Rect()
+
+ /**
+ * The cross fade progress at the start of the animation. 0.5f means it's just switching between
+ * the start and the end location and the content is fully faded, while 0.75f means that we're
+ * halfway faded in again in the target state.
+ */
+ private var animationStartCrossFadeProgress = 0.0f
+
+ /**
+ * The starting alpha of the animation
+ */
+ private var animationStartAlpha = 0.0f
+
+ /**
+ * The starting location of the cross fade if an animation is running right now.
+ */
+ @MediaLocation
+ private var crossFadeAnimationStartLocation = -1
+
+ /**
+ * The end location of the cross fade if an animation is running right now.
+ */
+ @MediaLocation
+ private var crossFadeAnimationEndLocation = -1
private var targetBounds: Rect = Rect()
private val mediaFrame
get() = mediaCarouselController.mediaFrame
@@ -98,9 +125,22 @@
interpolator = Interpolators.FAST_OUT_SLOW_IN
addUpdateListener {
updateTargetState()
- interpolateBounds(animationStartBounds, targetBounds, animatedFraction,
+ val currentAlpha: Float
+ var boundsProgress = animatedFraction
+ if (isCrossFadeAnimatorRunning) {
+ animationCrossFadeProgress = MathUtils.lerp(animationStartCrossFadeProgress, 1.0f,
+ animatedFraction)
+ // When crossfading, let's keep the bounds at the right location during fading
+ boundsProgress = if (animationCrossFadeProgress < 0.5f) 0.0f else 1.0f
+ currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress,
+ instantlyShowAtEnd = false)
+ } else {
+ // If we're not crossfading, let's interpolate from the start alpha to 1.0f
+ currentAlpha = MathUtils.lerp(animationStartAlpha, 1.0f, animatedFraction)
+ }
+ interpolateBounds(animationStartBounds, targetBounds, boundsProgress,
result = currentBounds)
- applyState(currentBounds)
+ applyState(currentBounds, currentAlpha)
}
addListener(object : AnimatorListenerAdapter() {
private var cancelled: Boolean = false
@@ -112,6 +152,7 @@
}
override fun onAnimationEnd(animation: Animator?) {
+ isCrossFadeAnimatorRunning = false
if (!cancelled) {
applyTargetStateIfNotAnimating()
}
@@ -192,11 +233,6 @@
private var distanceForFullShadeTransition = 0
/**
- * Delay after which the media will start transitioning to the full shade on the lockscreen.
- */
- private var fullShadeTransitionDelay = 0
-
- /**
* The amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
* shade.
@@ -207,18 +243,33 @@
return
}
field = value
- if (bypassController.bypassEnabled) {
+ if (bypassController.bypassEnabled || statusbarState != StatusBarState.KEYGUARD) {
+ // No need to do all the calculations / updates below if we're not on the lockscreen
+ // or if we're bypassing.
return
}
- updateDesiredLocation()
+ updateDesiredLocation(forceNoAnimation = isCurrentlyFading())
if (value >= 0) {
updateTargetState()
+ // Setting the alpha directly, as the below call will use it to update the alpha
+ carouselAlpha = calculateAlphaFromCrossFade(field, instantlyShowAtEnd = true)
applyTargetStateIfNotAnimating()
}
}
+ /**
+ * Is there currently a cross-fade animation running driven by an animator?
+ */
+ private var isCrossFadeAnimatorRunning = false
+
+ /**
+ * Are we currently transitionioning from the lockscreen to the full shade
+ * [StatusBarState.SHADE_LOCKED] or [StatusBarState.SHADE]. Once the user has dragged down and
+ * the transition starts, this will no longer return true.
+ */
private val isTransitioningToFullShade: Boolean
- get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled
+ get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled &&
+ statusbarState == StatusBarState.KEYGUARD
/**
* Set the amount of pixels we have currently dragged down if we're transitioning to the full
@@ -227,14 +278,8 @@
fun setTransitionToFullShadeAmount(value: Float) {
// If we're transitioning starting on the shade_locked, we don't want any delay and rather
// have it aligned with the rest of the animation
- val delay = if (statusbarState == StatusBarState.KEYGUARD) {
- fullShadeTransitionDelay
- } else {
- 0
- }
- val progress = MathUtils.saturate((value - delay) /
- (distanceForFullShadeTransition - delay))
- fullShadeTransitionProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(progress)
+ val progress = MathUtils.saturate(value / distanceForFullShadeTransition)
+ fullShadeTransitionProgress = progress
}
/**
@@ -296,6 +341,49 @@
}
}
+ /**
+ * The current cross fade progress. 0.5f means it's just switching
+ * between the start and the end location and the content is fully faded, while 0.75f means
+ * that we're halfway faded in again in the target state.
+ * This is only valid while [isCrossFadeAnimatorRunning] is true.
+ */
+ private var animationCrossFadeProgress = 1.0f
+
+ /**
+ * The current carousel Alpha.
+ */
+ private var carouselAlpha: Float = 1.0f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ CrossFadeHelper.fadeIn(mediaFrame, value)
+ }
+
+ /**
+ * Calculate the alpha of the view when given a cross-fade progress.
+ *
+ * @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching
+ * between the start and the end location and the content is fully faded, while 0.75f means
+ * that we're halfway faded in again in the target state.
+ *
+ * @param instantlyShowAtEnd should the view be instantly shown at the end. This is needed
+ * to avoid fadinging in when the target was hidden anyway.
+ */
+ private fun calculateAlphaFromCrossFade(
+ crossFadeProgress: Float,
+ instantlyShowAtEnd: Boolean
+ ): Float {
+ if (crossFadeProgress <= 0.5f) {
+ return 1.0f - crossFadeProgress / 0.5f
+ } else if (instantlyShowAtEnd) {
+ return 1.0f
+ } else {
+ return (crossFadeProgress - 0.5f) / 0.5f
+ }
+ }
+
init {
updateConfiguration()
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
@@ -375,9 +463,7 @@
private fun updateConfiguration() {
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_qs_transition_distance)
- fullShadeTransitionDelay = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_media_transition_start_delay)
+ R.dimen.lockscreen_shade_media_transition_distance)
}
/**
@@ -449,8 +535,13 @@
shouldAnimateTransition(desiredLocation, previousLocation)
val (animDuration, delay) = getAnimationParams(previousLocation, desiredLocation)
val host = getHost(desiredLocation)
- mediaCarouselController.onDesiredLocationChanged(desiredLocation, host, animate,
- animDuration, delay)
+ val willFade = calculateTransformationType() == TRANSFORMATION_TYPE_FADE
+ if (!willFade || isCurrentlyInGuidedTransformation() || !animate) {
+ // if we're fading, we want the desired location / measurement only to change
+ // once fully faded. This is happening in the host attachment
+ mediaCarouselController.onDesiredLocationChanged(desiredLocation, host,
+ animate, animDuration, delay)
+ }
performTransitionToNewLocation(isNewView, animate)
}
}
@@ -470,6 +561,8 @@
if (isCurrentlyInGuidedTransformation()) {
applyTargetStateIfNotAnimating()
} else if (animate) {
+ val wasCrossFading = isCrossFadeAnimatorRunning
+ val previewsCrossFadeProgress = animationCrossFadeProgress
animator.cancel()
if (currentAttachmentLocation != previousLocation ||
!previousHost.hostView.isAttachedToWindow) {
@@ -482,6 +575,42 @@
// be outdated
animationStartBounds.set(previousHost.currentBounds)
}
+ val transformationType = calculateTransformationType()
+ var needsCrossFade = transformationType == TRANSFORMATION_TYPE_FADE
+ var crossFadeStartProgress = 0.0f
+ // The alpha is only relevant when not cross fading
+ var newCrossFadeStartLocation = previousLocation
+ if (wasCrossFading) {
+ if (currentAttachmentLocation == crossFadeAnimationEndLocation) {
+ if (needsCrossFade) {
+ // We were previously crossFading and we've already reached
+ // the end view, Let's start crossfading from the same position there
+ crossFadeStartProgress = 1.0f - previewsCrossFadeProgress
+ }
+ // Otherwise let's fade in from the current alpha, but not cross fade
+ } else {
+ // We haven't reached the previous location yet, let's still cross fade from
+ // where we were.
+ newCrossFadeStartLocation = crossFadeAnimationStartLocation
+ if (newCrossFadeStartLocation == desiredLocation) {
+ // we're crossFading back to where we were, let's start at the end position
+ crossFadeStartProgress = 1.0f - previewsCrossFadeProgress
+ } else {
+ // Let's start from where we are right now
+ crossFadeStartProgress = previewsCrossFadeProgress
+ // We need to force cross fading as we haven't reached the end location yet
+ needsCrossFade = true
+ }
+ }
+ } else if (needsCrossFade) {
+ // let's not flicker and start with the same alpha
+ crossFadeStartProgress = (1.0f - carouselAlpha) / 2.0f
+ }
+ isCrossFadeAnimatorRunning = needsCrossFade
+ crossFadeAnimationStartLocation = newCrossFadeStartLocation
+ crossFadeAnimationEndLocation = desiredLocation
+ animationStartAlpha = carouselAlpha
+ animationStartCrossFadeProgress = crossFadeStartProgress
adjustAnimatorForTransition(desiredLocation, previousLocation)
if (!animationPending) {
rootView?.let {
@@ -518,6 +647,17 @@
// non-trivial reattaching logic happening that will make the view not-shown earlier
return true
}
+
+ if (statusbarState == StatusBarState.KEYGUARD) {
+ if (currentLocation == LOCATION_LOCKSCREEN &&
+ previousLocation == LOCATION_QS ||
+ (currentLocation == LOCATION_QS &&
+ previousLocation == LOCATION_LOCKSCREEN)) {
+ // We're always fading from lockscreen to keyguard in situations where the player
+ // is already fully hidden
+ return false
+ }
+ }
return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
}
@@ -538,7 +678,7 @@
keyguardStateController.isKeyguardFadingAway) {
delay = keyguardStateController.keyguardFadingAwayDelay
}
- animDuration = StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE.toLong()
+ animDuration = (StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE / 2f).toLong()
} else if (previousLocation == LOCATION_QQS && desiredLocation == LOCATION_LOCKSCREEN) {
animDuration = StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR.toLong()
}
@@ -550,7 +690,7 @@
// Let's immediately apply the target state (which is interpolated) if there is
// no animation running. Otherwise the animation update will already update
// the location
- applyState(targetBounds)
+ applyState(targetBounds, carouselAlpha)
}
}
@@ -558,7 +698,7 @@
* Updates the bounds that the view wants to be in at the end of the animation.
*/
private fun updateTargetState() {
- if (isCurrentlyInGuidedTransformation()) {
+ if (isCurrentlyInGuidedTransformation() && !isCurrentlyFading()) {
val progress = getTransformationProgress()
var endHost = getHost(desiredLocation)!!
var starthost = getHost(previousLocation)!!
@@ -606,12 +746,33 @@
}
/**
+ * Calculate the transformation type for the current animation
+ */
+ @VisibleForTesting
+ @TransformationType
+ fun calculateTransformationType(): Int {
+ if (isTransitioningToFullShade) {
+ return TRANSFORMATION_TYPE_FADE
+ }
+ if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS ||
+ previousLocation == LOCATION_QS && desiredLocation == LOCATION_LOCKSCREEN) {
+ // animating between ls and qs should fade, as QS is clipped.
+ return TRANSFORMATION_TYPE_FADE
+ }
+ if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QQS) {
+ // animating between ls and qqs should fade when dragging down via e.g. expand button
+ return TRANSFORMATION_TYPE_FADE
+ }
+ return TRANSFORMATION_TYPE_TRANSITION
+ }
+
+ /**
* @return the current transformation progress if we're in a guided transformation and -1
* otherwise
*/
private fun getTransformationProgress(): Float {
val progress = getQSTransformationProgress()
- if (progress >= 0) {
+ if (statusbarState != StatusBarState.KEYGUARD && progress >= 0) {
return progress
}
if (isTransitioningToFullShade) {
@@ -643,19 +804,20 @@
private fun cancelAnimationAndApplyDesiredState() {
animator.cancel()
getHost(desiredLocation)?.let {
- applyState(it.currentBounds, immediately = true)
+ applyState(it.currentBounds, alpha = 1.0f, immediately = true)
}
}
/**
* Apply the current state to the view, updating it's bounds and desired state
*/
- private fun applyState(bounds: Rect, immediately: Boolean = false) {
+ private fun applyState(bounds: Rect, alpha: Float, immediately: Boolean = false) {
currentBounds.set(bounds)
- val currentlyInGuidedTransformation = isCurrentlyInGuidedTransformation()
- val startLocation = if (currentlyInGuidedTransformation) previousLocation else -1
- val progress = if (currentlyInGuidedTransformation) getTransformationProgress() else 1.0f
- val endLocation = desiredLocation
+ carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
+ val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading()
+ val startLocation = if (onlyUseEndState) -1 else previousLocation
+ val progress = if (onlyUseEndState) 1.0f else getTransformationProgress()
+ val endLocation = resolveLocationForFading()
mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately)
updateHostAttachment()
if (currentAttachmentLocation == IN_OVERLAY) {
@@ -668,8 +830,19 @@
}
private fun updateHostAttachment() {
- val inOverlay = isTransitionRunning() && rootOverlay != null
- val newLocation = if (inOverlay) IN_OVERLAY else desiredLocation
+ var newLocation = resolveLocationForFading()
+ var canUseOverlay = !isCurrentlyFading()
+ if (isCrossFadeAnimatorRunning) {
+ if (getHost(newLocation)?.visible == true &&
+ getHost(newLocation)?.hostView?.isShown == false &&
+ newLocation != desiredLocation) {
+ // We're crossfading but the view is already hidden. Let's move to the overlay
+ // instead. This happens when animating to the full shade using a button click.
+ canUseOverlay = true
+ }
+ }
+ val inOverlay = isTransitionRunning() && rootOverlay != null && canUseOverlay
+ newLocation = if (inOverlay) IN_OVERLAY else newLocation
if (currentAttachmentLocation != newLocation) {
currentAttachmentLocation = newLocation
@@ -677,10 +850,10 @@
(mediaFrame.parent as ViewGroup?)?.removeView(mediaFrame)
// Add it to the new one
- val targetHost = getHost(desiredLocation)!!.hostView
if (inOverlay) {
rootOverlay!!.add(mediaFrame)
} else {
+ val targetHost = getHost(newLocation)!!.hostView
// When adding back to the host, let's make sure to reset the bounds.
// Usually adding the view will trigger a layout that does this automatically,
// but we sometimes suppress this.
@@ -693,9 +866,39 @@
left + currentBounds.width(),
top + currentBounds.height())
}
+ if (isCrossFadeAnimatorRunning) {
+ // When cross-fading with an animation, we only notify the media carousel of the
+ // location change, once the view is reattached to the new place and not immediately
+ // when the desired location changes. This callback will update the measurement
+ // of the carousel, only once we've faded out at the old location and then reattach
+ // to fade it in at the new location.
+ mediaCarouselController.onDesiredLocationChanged(
+ newLocation,
+ getHost(newLocation),
+ animate = false
+ )
+ }
}
}
+ /**
+ * Calculate the location when cross fading between locations. While fading out,
+ * the content should remain in the previous location, while after the switch it should
+ * be at the desired location.
+ */
+ private fun resolveLocationForFading(): Int {
+ if (isCrossFadeAnimatorRunning) {
+ // When animating between two hosts with a fade, let's keep ourselves in the old
+ // location for the first half, and then switch over to the end location
+ if (animationCrossFadeProgress > 0.5 || previousLocation == -1) {
+ return crossFadeAnimationEndLocation
+ } else {
+ return crossFadeAnimationStartLocation
+ }
+ }
+ return desiredLocation
+ }
+
private fun isTransitionRunning(): Boolean {
return isCurrentlyInGuidedTransformation() && getTransformationProgress() != 1.0f ||
animator.isRunning || animationPending
@@ -708,29 +911,29 @@
return desiredLocation
}
val onLockscreen = (!bypassController.bypassEnabled &&
- (statusbarState == StatusBarState.KEYGUARD ||
- statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
+ (statusbarState == StatusBarState.KEYGUARD ||
+ statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
- onLockscreen && isTransitioningToFullShade -> LOCATION_QQS
+ onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
}
// When we're on lock screen and the player is not active, we should keep it in QS.
// Otherwise it will try to animate a transition that doesn't make sense.
if (location == LOCATION_LOCKSCREEN && getHost(location)?.visible != true &&
- !statusBarStateController.isDozing) {
+ !statusBarStateController.isDozing) {
return LOCATION_QS
}
if (location == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS &&
- collapsingShadeFromQS) {
+ collapsingShadeFromQS) {
// When collapsing on the lockscreen, we want to remain in QS
return LOCATION_QS
}
if (location != LOCATION_LOCKSCREEN && desiredLocation == LOCATION_LOCKSCREEN &&
- !fullyAwake) {
+ !fullyAwake) {
// When unlocking from dozing / while waking up, the media shouldn't be transitioning
// in an animated way. Let's keep it in the lockscreen until we're fully awake and
// reattach it without an animation
@@ -740,6 +943,26 @@
}
/**
+ * Are we currently transforming to the full shade and already in QQS
+ */
+ private fun isTransformingToFullShadeAndInQQS(): Boolean {
+ if (!isTransitioningToFullShade) {
+ return false
+ }
+ return fullShadeTransitionProgress > 0.5f
+ }
+
+ /**
+ * Is the current transformationType fading
+ */
+ private fun isCurrentlyFading(): Boolean {
+ if (isTransitioningToFullShade) {
+ return true
+ }
+ return isCrossFadeAnimatorRunning
+ }
+
+ /**
* Returns true when the media card could be visible to the user if existed.
*/
private fun isVisibleToUser(): Boolean {
@@ -789,9 +1012,27 @@
* Attached at the root of the hierarchy in an overlay
*/
const val IN_OVERLAY = -1000
+
+ /**
+ * The default transformation type where the hosts transform into each other using a direct
+ * transition
+ */
+ const val TRANSFORMATION_TYPE_TRANSITION = 0
+
+ /**
+ * A transformation type where content fades from one place to another instead of
+ * transitioning
+ */
+ const val TRANSFORMATION_TYPE_FADE = 1
}
}
+@IntDef(prefix = ["TRANSFORMATION_TYPE_"], value = [
+ MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION,
+ MediaHierarchyManager.TRANSFORMATION_TYPE_FADE])
+@Retention(AnnotationRetention.SOURCE)
+private annotation class TransformationType
+
@IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS,
MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN])
@Retention(AnnotationRetention.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 34c654c..1c5fa43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -95,7 +95,7 @@
private boolean mLastKeyguardAndExpanded;
/**
* The last received state from the controller. This should not be used directly to check if
- * we're on keyguard but use {@link #isKeyguardShowing()} instead since that is more accurate
+ * we're on keyguard but use {@link #isKeyguardState()} instead since that is more accurate
* during state transitions which often call into us.
*/
private int mState;
@@ -326,7 +326,7 @@
|| mHeaderAnimating;
mQSPanelController.setExpanded(mQsExpanded);
mQSDetail.setExpanded(mQsExpanded);
- boolean keyguardShowing = isKeyguardShowing();
+ boolean keyguardShowing = isKeyguardState();
mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
|| mShowCollapsedOnKeyguard)
? View.VISIBLE
@@ -344,7 +344,7 @@
!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
}
- private boolean isKeyguardShowing() {
+ private boolean isKeyguardState() {
// We want the freshest state here since otherwise we'll have some weirdness if earlier
// listeners trigger updates
return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
@@ -366,7 +366,7 @@
if (mQSAnimator != null) {
mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
}
- if (!showCollapsed && isKeyguardShowing()) {
+ if (!showCollapsed && isKeyguardState()) {
setQsExpansion(mLastQSExpansion, 0);
}
}
@@ -457,7 +457,7 @@
mContainer.setExpansion(expansion);
final float translationScaleY = (mTranslateWhileExpanding
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
- boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard;
+ boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard;
if (!mHeaderAnimating && !headerWillBeAnimating()) {
getView().setTranslationY(
onKeyguardAndExpanded
@@ -531,7 +531,6 @@
// The Media can be scrolled off screen by default, let's offset it
float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
+ mQSPanelScrollView.getScrollRange();
- // The expanded media host should never move below the laid out position
pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */);
// The expanded media host should never move above the laid out position
pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */);
@@ -540,7 +539,8 @@
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
View hostView = mediaHost.getHostView();
- if (mLastQSExpansion > 0) {
+ // on keyguard we cross-fade to expanded, so no need to pin it.
+ if (mLastQSExpansion > 0 && !isKeyguardState()) {
float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- hostView.getHeight();
float currentPosition = mediaHost.getCurrentBounds().top
@@ -573,7 +573,7 @@
private boolean headerWillBeAnimating() {
return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard
- && !isKeyguardShowing();
+ && !isKeyguardState();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index be7adbc..74d3425 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -430,12 +430,14 @@
} else {
state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
}
- } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
- state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
- } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
- state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+ } else if (cb.mNoDefaultNetwork) {
+ if (cb.mNoNetworksAvailable) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
+ } else {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+ }
} else if (cb.mIsTransient) {
state.icon = ResourceIcon.get(
com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
@@ -488,12 +490,14 @@
state.state = Tile.STATE_INACTIVE;
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
- } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
- state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
- } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
- state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+ } else if (cb.mNoDefaultNetwork) {
+ if (cb.mNoNetworksAvailable) {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
+ } else {
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
+ state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+ }
} else {
state.icon = new SignalIcon(cb.mMobileSignalIconId);
state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index b6357b8..045a197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -36,6 +36,12 @@
*/
default void registerCallback(StatusBarWindowCallback callback) {}
+ /**
+ * Unregisters a {@link StatusBarWindowCallback previous registered with
+ * {@link #registerCallback(StatusBarWindowCallback)}}
+ */
+ default void unregisterCallback(StatusBarWindowCallback callback) {}
+
/** Notifies the registered {@link StatusBarWindowCallback} instances. */
default void notifyStateChangedCallbacks() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 655ed41..33aa7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -26,14 +26,18 @@
import android.provider.DeviceConfig
import android.util.Log
import android.view.View
+import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.util.Assert
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
@@ -59,9 +63,10 @@
private val coordinator: SystemEventCoordinator,
private val chipAnimationController: SystemEventChipAnimationController,
private val statusBarWindowController: StatusBarWindowController,
+ private val dumpManager: DumpManager,
private val systemClock: SystemClock,
@Main private val executor: DelayableExecutor
-) : CallbackController<SystemStatusAnimationCallback> {
+) : CallbackController<SystemStatusAnimationCallback>, Dumpable {
companion object {
private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator"
@@ -71,10 +76,6 @@
PROPERTY_ENABLE_IMMERSIVE_INDICATOR, true)
}
- /** True from the time a scheduled event starts until it's animation finishes */
- var isActive = false
- private set
-
@SystemAnimationState var animationState: Int = IDLE
private set
@@ -88,6 +89,7 @@
init {
coordinator.attachScheduler(this)
+ dumpManager.registerDumpable(TAG, this)
}
fun onStatusEvent(event: StatusEvent) {
@@ -293,6 +295,20 @@
anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
}
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("Scheduled event: $scheduledEvent")
+ pw.println("Has persistent privacy dot: $hasPersistentDot")
+ pw.println("Animation state: $animationState")
+ pw.println("Listeners:")
+ if (listeners.isEmpty()) {
+ pw.println("(none)")
+ } else {
+ listeners.forEach {
+ pw.println(" $it")
+ }
+ }
+ }
+
inner class ChipAnimatorAdapter(
@SystemAnimationState val endState: Int,
val viewCreator: (context: Context) -> View
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 21d8164..94edbd0 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
@@ -5145,8 +5145,8 @@
}
/**
- * Sets the extra top inset for the full shade transition. This is needed to compensate for
- * media transitioning to quick settings
+ * Sets the extra top inset for the full shade transition. This moves notifications down
+ * during the drag down.
*/
public void setExtraTopInsetForFullShadeTransition(float inset) {
mExtraTopInsetForFullShadeTransition = inset;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d23a309..4432f54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -185,8 +185,17 @@
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
+ /**
+ * The total distance in pixels that the full shade transition takes to transition entirely to
+ * the full shade.
+ */
private int mTotalDistanceForFullShadeTransition;
- private int mTotalExtraMediaInsetFullShadeTransition;
+
+ /**
+ * The amount of movement the notifications do when transitioning to the full shade before
+ * reaching the overstrech
+ */
+ private int mNotificationDragDownMovement;
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -255,8 +264,8 @@
};
private void updateResources() {
- mTotalExtraMediaInsetFullShadeTransition = mResources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_transition_extra_media_inset);
+ mNotificationDragDownMovement = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_notification_movement);
mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
R.dimen.lockscreen_shade_qs_transition_distance);
}
@@ -1410,15 +1419,13 @@
* shade. 0.0f means we're not transitioning yet.
*/
public void setTransitionToFullShadeAmount(float amount) {
- float extraTopInset;
- MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
- if (view == null || view.getHeight() == 0
- || mStatusBarStateController.getState() != KEYGUARD) {
- extraTopInset = 0;
- } else {
- extraTopInset = MathUtils.saturate(amount / mTotalDistanceForFullShadeTransition);
- extraTopInset = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(extraTopInset);
- extraTopInset = extraTopInset * mTotalExtraMediaInsetFullShadeTransition;
+ float extraTopInset = 0.0f;
+ if (mStatusBarStateController.getState() == KEYGUARD) {
+ float overallProgress = MathUtils.saturate(amount / mView.getHeight());
+ float transitionProgress = Interpolators.getOvershootInterpolation(overallProgress,
+ 0.6f,
+ (float) mTotalDistanceForFullShadeTransition / (float) mView.getHeight());
+ extraTopInset = transitionProgress * mNotificationDragDownMovement;
}
mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index f4710f4..7c2723d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -325,7 +325,9 @@
*/
private float getClockAlpha(int y) {
float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount)));
- alphaKeyguard *= (1f - mQsExpansion);
+ float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f);
+ qsAlphaFactor = 1f - qsAlphaFactor;
+ alphaKeyguard *= qsAlphaFactor;
alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index d84bb90..68e2070 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -142,7 +142,6 @@
Animator yTranslate =
ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels());
yTranslate.setDuration(getFadeOutDuration());
- fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
animatorSet.playTogether(fadeOut, yTranslate);
return animatorSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 0f3af09..d9ba494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,6 +24,8 @@
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
+import com.android.systemui.R;
+
public class NotificationPanelView extends PanelView {
private static final boolean DEBUG = false;
@@ -92,6 +94,10 @@
mRtlChangeListener = listener;
}
+ public TapAgainView getTapAgainView() {
+ return findViewById(R.id.shade_falsing_tap_again);
+ }
+
interface RtlChangeListener {
void onRtlPropertielsChanged(int layoutDirection);
}
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 1263b10..7d4707e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -97,8 +97,8 @@
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -306,6 +306,7 @@
private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
private final QSDetailDisplayer mQSDetailDisplayer;
+ private final FragmentService mFragmentService;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
private final PrivacyDotViewController mPrivacyDotViewController;
@@ -314,6 +315,7 @@
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
private final int mMaxKeyguardNotifications;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final TapAgainViewController mTapAgainViewController;
private boolean mShouldUseSplitNotificationShade;
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
@@ -508,6 +510,13 @@
private float mSectionPadding;
/**
+ * The padding between the start of notifications and the qs boundary on the lockscreen.
+ * On lockscreen, notifications aren't inset this extra amount, but we still want the
+ * qs boundary to be padded.
+ */
+ private int mLockscreenNotificationQSPadding;
+
+ /**
* The amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
* shade. This value can also go beyond 1.1 when we're overshooting!
@@ -529,7 +538,7 @@
/**
* The maximum overshoot allowed for the top padding for the full shade transition
*/
- private int mMaxOverscrollAmountForDragDown;
+ private int mMaxOverscrollAmountForPulse;
/**
* Should we animate the next bounds update
@@ -573,6 +582,7 @@
private int mScrimCornerRadius;
private int mScreenCornerRadius;
private int mNotificationScrimPadding;
+ private boolean mQSAnimatingHiddenFromCollapsed;
private final QuickAccessWalletClient mQuickAccessWalletClient;
private final Executor mUiExecutor;
@@ -604,7 +614,12 @@
private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() {
@Override
public void onDoubleTapRequired() {
- showTransientIndication(R.string.notification_tap_again);
+ if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
+ mTapAgainViewController.show();
+ } else {
+ mKeyguardIndicationController.showTransientIndication(
+ R.string.notification_tap_again);
+ }
mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
};
@@ -653,6 +668,8 @@
QuickAccessWalletClient quickAccessWalletClient,
KeyguardMediaController keyguardMediaController,
PrivacyDotViewController privacyDotViewController,
+ TapAgainViewController tapAgainViewController,
+ FragmentService fragmentService,
@Main Executor uiExecutor,
SecureSettings secureSettings) {
super(view, falsingManager, dozeLog, keyguardStateController,
@@ -679,6 +696,7 @@
mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
+ mFragmentService = fragmentService;
mKeyguardUserSwitcherEnabled = mResources.getBoolean(
com.android.internal.R.bool.config_keyguardUserSwitcher);
mKeyguardQsUserSwitchEnabled =
@@ -705,6 +723,7 @@
mUserManager = userManager;
mMediaDataManager = mediaDataManager;
mQuickAccessWalletClient = quickAccessWalletClient;
+ mTapAgainViewController = tapAgainViewController;
mUiExecutor = uiExecutor;
mSecureSettings = secureSettings;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
@@ -812,7 +831,7 @@
amount -> {
float progress = amount / mView.getHeight();
float overstretch = Interpolators.getOvershootInterpolation(progress,
- (float) mMaxOverscrollAmountForDragDown / mView.getHeight(),
+ (float) mMaxOverscrollAmountForPulse / mView.getHeight(),
0.2f);
setOverStrechAmount(overstretch);
});
@@ -843,6 +862,8 @@
if (mShouldUseSplitNotificationShade) {
updateResources();
}
+
+ mTapAgainViewController.init();
}
@Override
@@ -873,14 +894,16 @@
R.dimen.heads_up_status_bar_padding);
mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
R.dimen.lockscreen_shade_qs_transition_distance);
- mMaxOverscrollAmountForDragDown = mResources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_max_top_overshoot);
+ mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize(
+ R.dimen.pulse_expansion_max_top_overshoot);
mScrimCornerRadius = mResources.getDimensionPixelSize(
R.dimen.notification_scrim_corner_radius);
mScreenCornerRadius = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius);
mNotificationScrimPadding = mResources.getDimensionPixelSize(
R.dimen.notification_side_paddings);
+ mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
+ R.dimen.notification_side_paddings);
}
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
@@ -1421,7 +1444,7 @@
}
mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
- if (animate) {
+ if (animate && !isFullyCollapsed()) {
animateCloseQs(true /* animateAway */);
} else {
closeQs();
@@ -1705,6 +1728,11 @@
}
private float computeQsExpansionFraction() {
+ if (mQSAnimatingHiddenFromCollapsed) {
+ // When hiding QS from collapsed state, the expansion can sometimes temporarily
+ // be larger than 0 because of the timing, leading to flickers.
+ return 0.0f;
+ }
return Math.min(
1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
- mQsMinExpansionHeight));
@@ -2367,7 +2395,6 @@
public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade;
mNotificationBoundsAnimationDelay = delay;
- float progress = MathUtils.saturate(pxAmount / mView.getHeight());
float endPosition = 0;
if (pxAmount > 0.0f) {
@@ -2375,29 +2402,28 @@
&& !mMediaDataManager.hasActiveMedia()) {
// No notifications are visible, let's animate to the height of qs instead
if (mQs != null) {
- // Let's interpolate to the header height
- endPosition = mQs.getHeader().getHeight();
+ // Let's interpolate to the header height instead of the top padding,
+ // because the toppadding is way too low because of the large clock.
+ // we still want to take into account the edgePosition though as that nicely
+ // overshoots in the stackscroller
+ endPosition = getQSEdgePosition()
+ - mNotificationStackScrollLayoutController.getTopPadding()
+ + mQs.getHeader().getHeight();
}
} else {
// Interpolating to the new bottom edge position!
- endPosition = getQSEdgePosition() - mOverStretchAmount;
-
- // If we have media, we need to put the boundary below it, as the media header
- // still uses the space during the transition.
- endPosition +=
- mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
+ endPosition = getQSEdgePosition()
+ + mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
+ if (isOnKeyguard()) {
+ endPosition -= mLockscreenNotificationQSPadding;
+ }
}
}
// Calculate the overshoot amount such that we're reaching the target after our desired
// distance, but only reach it fully once we drag a full shade length.
- float transitionProgress = 0;
- if (endPosition != 0 && progress != 0) {
- transitionProgress = Interpolators.getOvershootInterpolation(progress,
- mMaxOverscrollAmountForDragDown / endPosition,
- (float) mDistanceForQSFullShadeTransition / (float) mView.getHeight());
- }
- mTransitioningToFullShadeProgress = transitionProgress;
+ mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition));
int position = (int) MathUtils.lerp((float) 0, endPosition,
mTransitioningToFullShadeProgress);
@@ -2405,8 +2431,6 @@
// we want at least 1 pixel otherwise the panel won't be clipped
position = Math.max(1, position);
}
- float overStretchAmount = Math.max(position - endPosition, 0.0f);
- setOverStrechAmount(overStretchAmount);
mTransitionToFullShadeQSPosition = position;
updateQsExpansion();
}
@@ -2509,6 +2533,7 @@
@Override
public void onAnimationEnd(Animator animation) {
+ mQSAnimatingHiddenFromCollapsed = false;
mAnimatingQS = false;
notifyExpandingFinished();
mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
@@ -2525,6 +2550,7 @@
animator.start();
mQsExpansionAnimator = animator;
mQsAnimatorExpand = expanding;
+ mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0;
}
/**
@@ -3675,10 +3701,6 @@
updateMaxDisplayedNotifications(true);
}
- public void showTransientIndication(int id) {
- mKeyguardIndicationController.showTransientIndication(id);
- }
-
public void setAlpha(float alpha) {
mView.setAlpha(alpha);
}
@@ -4264,7 +4286,8 @@
private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
@Override
public void onViewAttachedToWindow(View v) {
- FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
+ mFragmentService.getFragmentHostManager(mView)
+ .addTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
@@ -4278,7 +4301,8 @@
@Override
public void onViewDetachedFromWindow(View v) {
- FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener);
+ mFragmentService.getFragmentHostManager(mView)
+ .removeTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index ae018ba..52f9aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -152,6 +152,16 @@
mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
}
+ @Override
+ public void unregisterCallback(StatusBarWindowCallback callback) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks.get(i).get() == callback) {
+ mCallbacks.remove(i);
+ return;
+ }
+ }
+ }
+
/**
* Register a listener to monitor scrims visibility
* @param listener A listener to monitor scrims visibility
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 ba2340e..3f889e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4386,6 +4386,7 @@
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
+ updateLightRevealScrimVisibility();
Trace.endSection();
}
@@ -4818,6 +4819,11 @@
return;
}
+ if (mDozeServiceHost.isPulsing()) {
+ mLightRevealScrim.setVisibility(View.GONE);
+ return;
+ }
+
if (mFeatureFlags.useNewLockscreenAnimations()
&& (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
mLightRevealScrim.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 8befe80..edcf261 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -23,6 +23,8 @@
import android.view.DisplayCutout
import android.view.View.LAYOUT_DIRECTION_RTL
import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
@@ -118,17 +120,8 @@
val chipWidth = rotatedResources.getDimensionPixelSize(
R.dimen.ongoing_appops_chip_max_width)
- return if (context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL) {
- Rect(insets.left - dotWidth,
- insets.top,
- insets.left + chipWidth,
- insets.bottom)
- } else {
- Rect(insets.right - chipWidth,
- insets.top,
- insets.right + dotWidth,
- insets.bottom)
- }
+ val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+ return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
}
/**
@@ -139,8 +132,7 @@
var insets = insetsByCorner[rotation]
if (insets == null) {
val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
- insets = getCalculatedInsetsForRotation(rotation, rotatedResources)
- insetsByCorner[rotation] = insets
+ insets = getAndSetInsetsForRotation(rotation, rotatedResources)
}
return insets
@@ -157,13 +149,19 @@
}
private fun getCalculatedInsetsForRotation(
- @Rotation rotation: Int,
+ @Rotation targetRotation: Int,
rotatedResources: Resources
): Rect {
val dc = context.display.cutout
+ val currentRotation = RotationUtils.getExactRotation(context)
return calculateInsetsForRotationWithRotatedResources(
- rotation, rotatedResources, dc, windowManager, context)
+ currentRotation,
+ targetRotation,
+ dc,
+ windowManager.maximumWindowMetrics,
+ rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
+ rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding))
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
@@ -179,8 +177,8 @@
private const val TAG = "StatusBarInsetsProvider"
-private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotation: Int): Rect {
- val bounds = wm.maximumWindowMetrics.bounds
+private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect {
+ val bounds = wm.bounds
if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
return bounds
@@ -190,9 +188,24 @@
return Rect(0, 0, bounds.bottom, bounds.right)
}
-private fun getCurrentDisplayBounds(wm: WindowManager): Rect {
- val bounds = wm.maximumWindowMetrics.bounds
- return bounds
+@VisibleForTesting
+fun getPrivacyChipBoundingRectForInsets(
+ contentRect: Rect,
+ dotWidth: Int,
+ chipWidth: Int,
+ isRtl: Boolean
+): Rect {
+ return if (isRtl) {
+ Rect(contentRect.left - dotWidth,
+ contentRect.top,
+ contentRect.left + chipWidth,
+ contentRect.bottom)
+ } else {
+ Rect(contentRect.right - chipWidth,
+ contentRect.top,
+ contentRect.right + dotWidth,
+ contentRect.bottom)
+ }
}
/**
@@ -206,41 +219,32 @@
* @see [RotationUtils#getResourcesForRotation]
*/
fun calculateInsetsForRotationWithRotatedResources(
+ @Rotation currentRotation: Int,
@Rotation targetRotation: Int,
- rotatedResources: Resources,
displayCutout: DisplayCutout?,
- windowmanager: WindowManager,
- context: Context
+ windowMetrics: WindowMetrics,
+ statusBarHeight: Int,
+ roundedCornerPadding: Int
): Rect {
- val rtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
-
- val exactRotation = RotationUtils.getExactRotation(context)
- val height = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height)
-
/*
TODO: Check if this is ever used for devices with no rounded corners
- val paddingStart = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_start)
- val paddingEnd = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_end)
- val left = if (rtl) paddingEnd else paddingStart
- val right = if(rtl) paddingStart else paddingEnd
+ val left = if (isRtl) paddingEnd else paddingStart
+ val right = if (isRtl) paddingStart else paddingEnd
*/
- val roundedCornerPadding = rotatedResources.getDimensionPixelSize(
- R.dimen.rounded_corner_content_padding)
-
- val rotZeroBounds = getRotationZeroDisplayBounds(windowmanager, exactRotation)
- val currentBounds = getCurrentDisplayBounds(windowmanager)
+ val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation)
+ val currentBounds = windowMetrics.bounds
val sbLeftRight = getStatusBarLeftRight(
displayCutout,
- height,
+ statusBarHeight,
rotZeroBounds.right,
rotZeroBounds.bottom,
currentBounds.width(),
currentBounds.height(),
roundedCornerPadding,
targetRotation,
- exactRotation)
+ currentRotation)
return sbLeftRight
}
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 b10e841..d3953df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -175,7 +175,7 @@
&& !mIsAirplaneMode) {
newState.visible = true;
newState.resId = R.drawable.ic_qs_no_internet_unavailable;
- } else if (mWifiIconState.noValidatedNetwork && !mWifiIconState.noNetworksAvailable
+ } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable
&& (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
newState.visible = true;
newState.resId = R.drawable.ic_qs_no_internet_available;
@@ -380,7 +380,7 @@
if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
newState.visible = true;
newState.resId = R.drawable.ic_qs_no_internet_unavailable;
- } else if (noValidatedNetwork && !noNetworksAvailable
+ } else if (noDefaultNetwork && !noNetworksAvailable
&& (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
newState.visible = true;
newState.resId = R.drawable.ic_qs_no_internet_available;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
new file mode 100644
index 0000000..9856795
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.wm.shell.animation.Interpolators;
+
+/**
+ * View to show a toast-like popup on the notification shade and quick settings.
+ */
+public class TapAgainView extends FrameLayout {
+ public TapAgainView(
+ @NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ updateBgColor();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ TextView text = new TextView(mContext);
+ text.setText(R.string.notification_tap_again);
+ addView(text);
+ }
+
+ void updateBgColor() {
+ setBackgroundResource(R.drawable.rounded_bg_full);
+ }
+
+ /** Make the view visible. */
+ public void animateIn() {
+ int yTranslation = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_y_translation);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f);
+ fadeIn.setStartDelay(150); // From KeyguardIndicationTextView#getFadeInDelay
+ fadeIn.setDuration(317); // From KeyguardIndicationTextView#getFadeInDuration
+ fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+
+ Animator yTranslate =
+ ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, yTranslation, 0);
+ yTranslate.setDuration(600); // From KeyguardIndicationTextView#getYInDuration
+ yTranslate.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ setTranslationY(0);
+ }
+ });
+ animatorSet.playTogether(yTranslate, fadeIn);
+ animatorSet.start();
+ setVisibility(View.VISIBLE);
+ }
+
+ /** Make the view gone. */
+ public void animateOut() {
+ long fadeOutDuration = 167L; // From KeyguardIndicationTextView#getFadeOutDuration
+ int yTranslation = mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen.keyguard_indication_y_translation);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ ObjectAnimator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
+ fadeOut.setDuration(fadeOutDuration);
+ fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+
+ Animator yTranslate =
+ ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -yTranslation);
+ yTranslate.setDuration(fadeOutDuration);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setVisibility(GONE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ setVisibility(GONE);
+ }
+ });
+ animatorSet.playTogether(yTranslate, fadeOut);
+ animatorSet.start();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
new file mode 100644
index 0000000..bb53bad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Controller for {@link TapAgainView}.
+ */
+@StatusBarComponent.StatusBarScope
+public class TapAgainViewController extends ViewController<TapAgainView> {
+ private final DelayableExecutor mDelayableExecutor;
+ private final ConfigurationController mConfigurationController;
+ private final long mDoubleTapTimeMs;
+
+ private Runnable mHideCanceler;
+
+ @VisibleForTesting
+ final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onOverlayChanged() {
+ mView.updateBgColor();
+ }
+
+ @Override
+ public void onUiModeChanged() {
+ mView.updateBgColor();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ mView.updateBgColor();
+ }
+ };
+
+ @Inject
+ protected TapAgainViewController(TapAgainView view,
+ @Main DelayableExecutor delayableExecutor,
+ ConfigurationController configurationController,
+ @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs) {
+ super(view);
+ mDelayableExecutor = delayableExecutor;
+ mConfigurationController = configurationController;
+ mDoubleTapTimeMs = doubleTapTimeMs;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mConfigurationController.addCallback(mConfigurationListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mConfigurationController.removeCallback(mConfigurationListener);
+ }
+
+ /** Shows the associated view, possibly animating it. */
+ public void show() {
+ if (mHideCanceler != null) {
+ mHideCanceler.run();
+ }
+ mView.animateIn();
+ mHideCanceler = mDelayableExecutor.executeDelayed(this::hide, mDoubleTapTimeMs);
+ }
+
+ /** Hides the associated view, possibly animating it. */
+ public void hide() {
+ mHideCanceler = null;
+ mView.animateOut();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 008c0ae..27d71ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -23,6 +23,7 @@
import com.android.systemui.biometrics.AuthRippleView;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
+import com.android.systemui.statusbar.phone.TapAgainView;
import dagger.Module;
import dagger.Provides;
@@ -53,4 +54,11 @@
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.auth_ripple);
}
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static TapAgainView getTapAgainView(NotificationPanelView npv) {
+ return npv.getTapAgainView();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 07e9fed..4ab07af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -1023,6 +1023,11 @@
mValidatedTransports.clear();
if (mLastDefaultNetworkCapabilities != null) {
for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) {
+ if (transportType != NetworkCapabilities.TRANSPORT_CELLULAR
+ && transportType != NetworkCapabilities.TRANSPORT_WIFI
+ && transportType != NetworkCapabilities.TRANSPORT_ETHERNET) {
+ continue;
+ }
if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR
&& Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) {
mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI);
@@ -1045,11 +1050,15 @@
Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
}
- mInetCondition = !mValidatedTransports.isEmpty();
+ mInetCondition = mValidatedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+ || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+ || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
pushConnectivityToSignals();
if (mProviderModel) {
- mNoDefaultNetwork = mConnectedTransports.isEmpty();
+ mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
mNoNetworksAvailable);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index c6aef4a..bf87a4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -20,6 +20,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
@@ -33,6 +34,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
+import junit.framework.Assert
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
@@ -65,8 +67,6 @@
@Mock
private lateinit var bypassController: KeyguardBypassController
@Mock
- private lateinit var mediaFrame: ViewGroup
- @Mock
private lateinit var keyguardStateController: KeyguardStateController
@Mock
private lateinit var statusBarStateController: SysuiStatusBarStateController
@@ -90,9 +90,11 @@
@Rule
val mockito = MockitoJUnit.rule()
private lateinit var mediaHiearchyManager: MediaHierarchyManager
+ private lateinit var mediaFrame: ViewGroup
@Before
fun setup() {
+ mediaFrame = FrameLayout(context)
`when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
mediaHiearchyManager = MediaHierarchyManager(
context,
@@ -112,6 +114,9 @@
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
`when`(mediaCarouselController.mediaCarouselScrollHandler)
.thenReturn(mediaCarouselScrollHandler)
+ val observer = wakefullnessObserver.value
+ assertNotNull("lifecycle observer wasn't registered", observer)
+ observer.onFinishedWakingUp()
// We'll use the viewmanager to verify a few calls below, let's reset this.
clearInvocations(mediaCarouselController)
}
@@ -120,6 +125,7 @@
`when`(host.location).thenReturn(location)
`when`(host.currentBounds).thenReturn(Rect())
`when`(host.hostView).thenReturn(UniqueObjectHostView(context))
+ `when`(host.visible).thenReturn(true)
mediaHiearchyManager.register(host)
}
@@ -160,6 +166,73 @@
}
@Test
+ fun testGoingToFullShade() {
+ // Let's set it onto Lock screen
+ `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
+ true)
+ statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ clearInvocations(mediaCarouselController)
+
+ // Let's transition all the way to full shade
+ mediaHiearchyManager.setTransitionToFullShadeAmount(100000f)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QQS),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong())
+ clearInvocations(mediaCarouselController)
+
+ // Let's go back to the lock screen
+ mediaHiearchyManager.setTransitionToFullShadeAmount(0.0f)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_LOCKSCREEN),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong())
+
+ // Let's make sure alpha is set
+ mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f)
+ Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f)
+ }
+
+ @Test
+ fun testTransformationOnLockScreenIsFading() {
+ // Let's set it onto Lock screen
+ `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
+ true)
+ statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ clearInvocations(mediaCarouselController)
+
+ // Let's transition from lockscreen to qs
+ mediaHiearchyManager.qsExpansion = 1.0f
+ val transformType = mediaHiearchyManager.calculateTransformationType()
+ Assert.assertTrue("media isn't transforming to qs with a fade",
+ transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ }
+
+ @Test
+ fun testTransformationOnLockScreenToQQSisFading() {
+ // Let's set it onto Lock screen
+ `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
+ true)
+ statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ clearInvocations(mediaCarouselController)
+
+ // Let's transition from lockscreen to qs
+ `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD,
+ StatusBarState.SHADE_LOCKED)
+ val transformType = mediaHiearchyManager.calculateTransformationType()
+ Assert.assertTrue("media isn't transforming to qqswith a fade",
+ transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ }
+
+ @Test
fun testCloseGutsRelayToCarousel() {
mediaHiearchyManager.closeGuts()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 6b4797f..83a1872 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -20,12 +20,15 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+import static com.android.systemui.statusbar.notification.ViewGroupFadeHelper.reset;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -75,13 +78,17 @@
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -252,11 +259,21 @@
private PrivacyDotViewController mPrivacyDotViewController;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private TapAgainViewController mTapAgainViewController;
+ @Mock
+ private KeyguardIndicationController mKeyguardIndicationController;
+ @Mock
+ private FragmentService mFragmentService;
+ @Mock
+ private FragmentHostManager mFragmentHostManager;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
+ private FalsingManagerFake mFalsingManager = new FalsingManagerFake();
@Before
public void setup() {
@@ -297,6 +314,7 @@
mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view));
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
+ when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
@@ -317,7 +335,7 @@
mKeyguardBypassController, mHeadsUpManager,
mock(NotificationRoundnessManager.class),
mStatusBarStateController,
- new FalsingManagerFake(),
+ mFalsingManager,
mLockscreenShadeTransitionController,
new FalsingCollectorFake());
when(mKeyguardStatusViewComponentFactory.build(any()))
@@ -331,11 +349,12 @@
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
+ reset(mView);
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
mLayoutInflater,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
- new FalsingManagerFake(), new FalsingCollectorFake(),
+ mFalsingManager, new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
mKeyguardStateController, mStatusBarStateController, mDozeLog,
mDozeParameters, mCommandQueue, mVibratorHelper,
@@ -364,6 +383,8 @@
mQuickAccessWalletClient,
mKeyguardMediaController,
mPrivacyDotViewController,
+ mTapAgainViewController,
+ mFragmentService,
new FakeExecutor(new FakeSystemClock()),
mSecureSettings);
mNotificationPanelViewController.initDependencies(
@@ -371,6 +392,13 @@
mNotificationShelfController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
mNotificationPanelViewController.setBar(mPanelBar);
+ mNotificationPanelViewController.setKeyguardIndicationController(
+ mKeyguardIndicationController);
+ ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ verify(mView, atLeast(1)).addOnAttachStateChangeListener(
+ onAttachStateChangeListenerArgumentCaptor.capture());
+ mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues();
ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor =
ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
@@ -616,6 +644,34 @@
verify(mKeyguardStateController).notifyPanelFlingEnd();
}
+ @Test
+ public void testDoubleTapRequired_Keyguard() {
+ FalsingManager.FalsingTapListener listener = getFalsingTapListener();
+ mStatusBarStateController.setState(KEYGUARD);
+
+ listener.onDoubleTapRequired();
+
+ verify(mKeyguardIndicationController).showTransientIndication(anyInt());
+ }
+
+ @Test
+ public void testDoubleTapRequired_ShadeLocked() {
+ FalsingManager.FalsingTapListener listener = getFalsingTapListener();
+ mStatusBarStateController.setState(SHADE_LOCKED);
+
+ listener.onDoubleTapRequired();
+
+ verify(mTapAgainViewController).show();
+ }
+
+ private FalsingManager.FalsingTapListener getFalsingTapListener() {
+ for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+ listener.onViewAttachedToWindow(mView);
+ }
+ assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1);
+ return mFalsingManager.getTapListeners().get(0);
+ }
+
private View newViewWithId(int id) {
View view = new View(mContext);
view.setId(id);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
new file mode 100644
index 0000000..4796cd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Rect
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.DisplayCutout
+import android.view.WindowMetrics
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.leak.RotationUtils
+import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
+import com.android.systemui.util.leak.RotationUtils.Rotation
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class StatusBarContentInsetsProviderTest : SysuiTestCase() {
+ @Mock private lateinit var dc: DisplayCutout
+ @Mock private lateinit var windowMetrics: WindowMetrics
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testGetBoundingRectForPrivacyChipForRotation_noCutout() {
+ val screenBounds = Rect(0, 0, 1080, 2160)
+ val roundedCornerPadding = 20
+ val sbHeightPortrait = 100
+ val sbHeightLandscape = 60
+ val currentRotation = ROTATION_NONE
+ val chipWidth = 30
+ val dotWidth = 10
+
+ `when`(windowMetrics.bounds).thenReturn(screenBounds)
+
+ var isRtl = false
+ var targetRotation = ROTATION_NONE
+ var bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ null,
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+
+ var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+ /* 1080 - 20 (rounded corner) - 30 (chip),
+ * 0 (sb top)
+ * 1080 - 20 (rounded corner) + 10 ( dot),
+ * 100 (sb height portrait)
+ */
+ var expected = Rect(1030, 0, 1070, 100)
+ assertRects(expected, chipBounds, currentRotation, targetRotation)
+ isRtl = true
+ chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+ /* 0 + 20 (rounded corner) - 10 (dot),
+ * 0 (sb top)
+ * 0 + 20 (rounded corner) + 30 (chip),
+ * 100 (sb height portrait)
+ */
+ expected = Rect(10, 0, 50, 100)
+ assertRects(expected, chipBounds, currentRotation, targetRotation)
+
+ isRtl = false
+ targetRotation = ROTATION_LANDSCAPE
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+
+ chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+ /* 2160 - 20 (rounded corner) - 30 (chip),
+ * 0 (sb top)
+ * 2160 - 20 (rounded corner) + 10 ( dot),
+ * 60 (sb height landscape)
+ */
+ expected = Rect(2110, 0, 2150, 60)
+ assertRects(expected, chipBounds, currentRotation, targetRotation)
+ isRtl = true
+ chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+ /* 0 + 20 (rounded corner) - 10 (dot),
+ * 0 (sb top)
+ * 0 + 20 (rounded corner) + 30 (chip),
+ * 60 (sb height landscape)
+ */
+ expected = Rect(10, 0, 50, 60)
+ assertRects(expected, chipBounds, currentRotation, targetRotation)
+ }
+
+ @Test
+ fun testCalculateInsetsForRotationWithRotatedResources_topLeftCutout() {
+ // GIVEN a device in portrait mode with width < height and a display cutout in the top-left
+ val screenBounds = Rect(0, 0, 1080, 2160)
+ val dcBounds = Rect(0, 0, 100, 100)
+ val roundedCornerPadding = 20
+ val sbHeightPortrait = 100
+ val sbHeightLandscape = 60
+ val currentRotation = ROTATION_NONE
+
+ `when`(windowMetrics.bounds).thenReturn(screenBounds)
+ `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
+
+ // THEN rotations which share a short side should use the greater value between rounded
+ // corner padding and the display cutout's size
+ var targetRotation = ROTATION_NONE
+ var expectedBounds = Rect(dcBounds.right,
+ 0,
+ screenBounds.right - roundedCornerPadding,
+ sbHeightPortrait)
+
+ var bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_LANDSCAPE
+ expectedBounds = Rect(dcBounds.height(),
+ 0,
+ screenBounds.height() - roundedCornerPadding,
+ sbHeightLandscape)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ // THEN the side that does NOT share a short side with the display cutout ignores the
+ // display cutout bounds
+ targetRotation = ROTATION_UPSIDE_DOWN
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.width() - roundedCornerPadding,
+ sbHeightPortrait)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ // Phone in portrait, seascape (rot_270) bounds
+ targetRotation = ROTATION_SEASCAPE
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.height() - dcBounds.height(),
+ sbHeightLandscape)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+ }
+
+ @Test
+ fun testCalculateInsetsForRotationWithRotatedResources_nonCornerCutout() {
+ // GIVEN phone in portrait mode, where width < height and the cutout is not in the corner
+ // the assumption here is that if the cutout does NOT touch the corner then we have room to
+ // layout the status bar in the given space.
+
+ val screenBounds = Rect(0, 0, 1080, 2160)
+ // cutout centered at the top
+ val dcBounds = Rect(490, 0, 590, 100)
+ val roundedCornerPadding = 20
+ val sbHeightPortrait = 100
+ val sbHeightLandscape = 60
+ val currentRotation = ROTATION_NONE
+
+ `when`(windowMetrics.bounds).thenReturn(screenBounds)
+ `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
+
+ // THEN only the landscape/seascape rotations should avoid the cutout area because of the
+ // potential letterboxing
+ var targetRotation = ROTATION_NONE
+ var expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.right - roundedCornerPadding,
+ sbHeightPortrait)
+
+ var bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_LANDSCAPE
+ expectedBounds = Rect(dcBounds.height(),
+ 0,
+ screenBounds.height() - roundedCornerPadding,
+ sbHeightLandscape)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_UPSIDE_DOWN
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.right - roundedCornerPadding,
+ sbHeightPortrait)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_SEASCAPE
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.height() - dcBounds.height(),
+ sbHeightLandscape)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+ }
+
+ @Test
+ fun testCalculateInsetsForRotationWithRotatedResources_noCutout() {
+ // GIVEN device in portrait mode, where width < height and no cutout
+ val currentRotation = ROTATION_NONE
+ val screenBounds = Rect(0, 0, 1080, 2160)
+ val roundedCornerPadding = 20
+ val sbHeightPortrait = 100
+ val sbHeightLandscape = 60
+
+ `when`(windowMetrics.bounds).thenReturn(screenBounds)
+
+ // THEN content insets should only use rounded corner padding
+ var targetRotation = ROTATION_NONE
+ var expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.right - roundedCornerPadding,
+ sbHeightPortrait)
+
+ var bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ null, /* no cutout */
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_LANDSCAPE
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.height() - roundedCornerPadding,
+ sbHeightLandscape)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ null, /* no cutout */
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_UPSIDE_DOWN
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.width() - roundedCornerPadding,
+ sbHeightPortrait)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ null, /* no cutout */
+ windowMetrics,
+ sbHeightPortrait,
+ roundedCornerPadding)
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+ targetRotation = ROTATION_LANDSCAPE
+ expectedBounds = Rect(roundedCornerPadding,
+ 0,
+ screenBounds.height() - roundedCornerPadding,
+ sbHeightLandscape)
+
+ bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ null, /* no cutout */
+ windowMetrics,
+ sbHeightLandscape,
+ roundedCornerPadding)
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+ }
+
+ private fun assertRects(
+ expected: Rect,
+ actual: Rect,
+ @Rotation currentRotation: Int,
+ @Rotation targetRotation: Int
+ ) {
+ assertTrue(
+ "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
+ " targetRotation=${RotationUtils.toString(targetRotation)}" +
+ " expected=$expected actual=$actual",
+ expected.equals(actual))
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6cb374a..4e6e91a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -771,9 +771,7 @@
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
- // TODO(b/187223764): busTime won't be needed once end_session is a field in BUS.
- final long busTime = System.currentTimeMillis();
- final byte[] statsProto = bus.getStatsProto(busTime);
+ final byte[] statsProto = bus.getStatsProto();
data.add(FrameworkStatsLog.buildStatsEvent(atomTag, statsProto));
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index d6060fa..88c9850 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -16,16 +16,12 @@
package com.android.server.power;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -74,8 +70,6 @@
private static final boolean DEBUG = false;
- private static final int MSG_ON_WAKE_LOCK_EVENT = 1;
-
private static final int TYPE_TIME_RESET = 0x0;
private static final int TYPE_ACQUIRE = 0x1;
private static final int TYPE_RELEASE = 0x2;
@@ -130,7 +124,6 @@
private final Injector mInjector;
private final TheLog mLog;
private final TagDatabase mTagDatabase;
- private final Handler mHandler;
private final SimpleDateFormat mDumpsysDateFormat;
WakeLockLog() {
@@ -140,7 +133,6 @@
@VisibleForTesting
WakeLockLog(Injector injector) {
mInjector = injector;
- mHandler = new WakeLockLogHandler(injector.getLooper());
mTagDatabase = new TagDatabase(injector);
EntryByteTranslator translator = new EntryByteTranslator(mTagDatabase);
mLog = new TheLog(injector, translator, mTagDatabase);
@@ -172,7 +164,7 @@
* Dumps all the wake lock data currently saved in the wake lock log to the specified
* {@code PrintWriter}.
*
- * @param The {@code PrintWriter} to write to.
+ * @param pw The {@code PrintWriter} to write to.
*/
public void dump(PrintWriter pw) {
dump(pw, false);
@@ -221,9 +213,6 @@
/**
* Adds a new entry to the log based on the specified wake lock parameters.
*
- * Grabs the current time for the event and then posts the rest of the logic (actually
- * adding it to the log) to a background thread.
- *
* @param eventType The type of event (ACQUIRE, RELEASE);
* @param tag The wake lock's identifying tag.
* @param ownerUid The owner UID of the wake lock.
@@ -239,15 +228,11 @@
}
final long time = mInjector.currentTimeMillis();
-
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = tagNameReducer(tag);
- args.argi1 = eventType;
- args.argi2 = ownerUid;
- args.argi3 = eventType == TYPE_ACQUIRE ? translateFlagsFromPowerManager(flags) : 0;
- args.argi4 = (int) ((time >> 32) & 0xFFFFFFFFL);
- args.argi5 = (int) (time & 0xFFFFFFFFL);
- mHandler.obtainMessage(MSG_ON_WAKE_LOCK_EVENT, args).sendToTarget();
+ final int translatedFlags = eventType == TYPE_ACQUIRE
+ ? translateFlagsFromPowerManager(flags)
+ : 0;
+ handleWakeLockEventInternal(eventType, tagNameReducer(tag), ownerUid, translatedFlags,
+ time);
}
/**
@@ -273,8 +258,8 @@
* flags, {@code WakeLockLog.FLAG_*}, and a log-level, {@code WakeLockLog.LEVEL_*}.
*
* @param flags Wake lock flags including {@code PowerManager.*_WAKE_LOCK}
- * {@link PowerManager.ACQUIRE_CAUSES_WAKEUP}, and
- * {@link PowerManager.ON_AFTER_RELEASE}.
+ * {@link PowerManager#ACQUIRE_CAUSES_WAKEUP}, and
+ * {@link PowerManager#ON_AFTER_RELEASE}.
* @return The compressed flags value.
*/
int translateFlagsFromPowerManager(int flags) {
@@ -328,9 +313,9 @@
}
String reduciblePrefix = null;
- for (int tp = 0; tp < REDUCED_TAG_PREFIXES.length; tp++) {
- if (tag.startsWith(REDUCED_TAG_PREFIXES[tp])) {
- reduciblePrefix = REDUCED_TAG_PREFIXES[tp];
+ for (String reducedTagPrefix : REDUCED_TAG_PREFIXES) {
+ if (tag.startsWith(reducedTagPrefix)) {
+ reduciblePrefix = reducedTagPrefix;
break;
}
}
@@ -339,7 +324,7 @@
final StringBuilder sb = new StringBuilder();
// add prefix first
- sb.append(tag.substring(0, reduciblePrefix.length()));
+ sb.append(tag, 0, reduciblePrefix.length());
// Stop looping on final marker
final int end = Math.max(tag.lastIndexOf("/"), tag.lastIndexOf("."));
@@ -604,7 +589,7 @@
* @return The number of bytes written to buffer, or required to write to the buffer.
*/
int toBytes(LogEntry entry, byte[] bytes, long timeReference) {
- int sizeNeeded = -1;
+ final int sizeNeeded;
switch (entry.type) {
case TYPE_ACQUIRE: {
sizeNeeded = 3;
@@ -696,8 +681,9 @@
* {@link EntryByteTranslator} to convert byte {@link LogEntry} to bytes within the buffer.
*
* This class also implements the logic around TIME_RESET events. Since the LogEntries store
- * their time (8-bit) relative to the previous event, this class can add {@link TYPE_TIME_RESET}
- * LogEntries as necessary to allow a LogEntry's relative time to fit within that range.
+ * their time (8-bit) relative to the previous event, this class can add
+ * {@link #TYPE_TIME_RESET} LogEntries as necessary to allow a LogEntry's relative time to fit
+ * within that range.
*/
static class TheLog {
private final EntryByteTranslator mTranslator;
@@ -711,7 +697,7 @@
/**
* Second temporary buffer used when reading and writing bytes from the buffer.
* A second temporary buffer is necessary since additional items can be read concurrently
- * from {@link mTempBuffer}. E.g., Adding an entry to a full buffer requires removing
+ * from {@link #mTempBuffer}. E.g., Adding an entry to a full buffer requires removing
* other entries from the buffer.
*/
private final byte[] mReadWriteTempBuffer = new byte[MAX_LOG_ENTRY_BYTE_SIZE];
@@ -832,7 +818,7 @@
* Returns an {@link Iterator} of {@link LogEntry}s for all the entries in the log.
*
* If the log is modified while the entries are being read, the iterator will throw a
- * {@link ConcurrentModificationExceptoin}.
+ * {@link ConcurrentModificationException}.
*
* @param tempEntry A temporary {@link LogEntry} instance to use so that new instances
* aren't allocated with every call to {@code Iterator.next}.
@@ -1063,7 +1049,7 @@
* instanced into bytes.
*
* If a new tag is added when the database is full, the oldest tag is removed. The oldest tag
- * is calcualted using {@link TagData.lastUsedTime}.
+ * is calculated using {@link TagData#lastUsedTime}.
*/
static class TagDatabase {
private final int mInvalidIndex;
@@ -1086,9 +1072,9 @@
int byteEstimate = 0;
int tagSize = 0;
int tags = 0;
- for (int i = 0; i < mArray.length; i++) {
+ for (TagData tagData : mArray) {
byteEstimate += 8; // reference pointer
- TagData data = mArray[i];
+ TagData data = tagData;
if (data != null) {
entries++;
byteEstimate += data.getByteSize();
@@ -1280,10 +1266,6 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- if (DEBUG) {
- sb.append("(").append(index).append(")");
- }
return "[" + ownerUid + " ; " + tag + "]";
}
@@ -1308,10 +1290,6 @@
* Injector used by {@link WakeLockLog} for testing purposes.
*/
public static class Injector {
- public Looper getLooper() {
- return BackgroundThread.get().getLooper();
- }
-
public int getTagDatabaseSize() {
return TAG_DATABASE_SIZE;
}
@@ -1328,28 +1306,4 @@
return DATE_FORMAT;
}
}
-
- private class WakeLockLogHandler extends Handler {
- WakeLockLogHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch(message.what) {
- case MSG_ON_WAKE_LOCK_EVENT:
- final SomeArgs args = (SomeArgs) message.obj;
- final String tag = (String) args.arg1;
- final int eventType = args.argi1;
- final int ownerUid = args.argi2;
- final int flags = args.argi3;
- final long time = (((long) args.argi4) << 32) + (args.argi5 & 0xFFFFFFFFL);
- args.recycle();
- handleWakeLockEventInternal(eventType, tag, ownerUid, flags, time);
- break;
- default:
- break;
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index a82c91e..dc868b3 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -126,6 +126,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.connectivity.WifiActivityEnergyInfo;
+import android.os.incremental.IncrementalManager;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
@@ -426,6 +427,7 @@
private final Object mHealthHalLock = new Object();
private final Object mAttributedAppOpsLock = new Object();
private final Object mSettingsStatsLock = new Object();
+ private final Object mInstalledIncrementalPackagesLock = new Object();
public StatsPullAtomService(Context context) {
super(context);
@@ -695,6 +697,10 @@
synchronized (mSettingsStatsLock) {
return pullSettingsStatsLocked(atomTag, data);
}
+ case FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE:
+ synchronized (mInstalledIncrementalPackagesLock) {
+ return pullInstalledIncrementalPackagesLocked(atomTag, data);
+ }
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -877,6 +883,7 @@
registerBatteryVoltage();
registerBatteryCycleCount();
registerSettingsStats();
+ registerInstalledIncrementalPackages();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -3949,6 +3956,31 @@
return StatsManager.PULL_SUCCESS;
}
+ private void registerInstalledIncrementalPackages() {
+ int tagId = FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ int pullInstalledIncrementalPackagesLocked(int atomTag, List<StatsEvent> pulledData) {
+ final PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY)) {
+ // Incremental is not enabled on this device. The result list will be empty.
+ return StatsManager.PULL_SUCCESS;
+ }
+ List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
+ for (PackageInfo pi : installedPackages) {
+ if (IncrementalManager.isIncrementalPath(pi.applicationInfo.getBaseCodePath())) {
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pi.applicationInfo.uid));
+ }
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
index a03ba9c..09612e3 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
@@ -20,12 +20,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.os.Looper;
import android.os.PowerManager;
-import android.os.test.TestLooper;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import java.io.PrintWriter;
@@ -38,17 +34,6 @@
*/
public class WakeLockLogTest {
- private TestLooper mTestLooper;
-
- @Before
- public void setUp() throws Exception {
- mTestLooper = new TestLooper();
- }
-
- @After
- public void tearDown() throws Exception {
- }
-
@Test
public void testAddTwoItems() {
final int tagDatabaseSize = 128;
@@ -70,7 +55,7 @@
+ " -\n"
+ " Events: 2, Time-Resets: 0\n"
+ " Buffer, Bytes used: 6\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
@Test
@@ -92,7 +77,7 @@
+ " -\n"
+ " Events: 2, Time-Resets: 1\n"
+ " Buffer, Bytes used: 15\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
@Test
@@ -114,7 +99,7 @@
+ " -\n"
+ " Events: 2, Time-Resets: 0\n"
+ " Buffer, Bytes used: 6\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
@Test
@@ -142,7 +127,7 @@
+ " -\n"
+ " Events: 3, Time-Resets: 0\n"
+ " Buffer, Bytes used: 9\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
@Test
@@ -160,7 +145,7 @@
+ " -\n"
+ " Events: 0, Time-Resets: 0\n"
+ " Buffer, Bytes used: 0\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
@Test
@@ -179,7 +164,7 @@
+ " -\n"
+ " Events: 1, Time-Resets: 0\n"
+ " Buffer, Bytes used: 3\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
@Test
@@ -201,7 +186,7 @@
+ " Events: 2, Time-Resets: 0\n"
+ " Buffer, Bytes used: 5\n"
+ " Tag Database: size(5), entries: 1, Bytes used: 80\n",
- dispatchAndDump(log, true));
+ dumpLog(log, true));
}
@Test
@@ -223,11 +208,10 @@
+ " -\n"
+ " Events: 1, Time-Resets: 0\n"
+ " Buffer, Bytes used: 3\n",
- dispatchAndDump(log, false));
+ dumpLog(log, false));
}
- private String dispatchAndDump(WakeLockLog log, boolean includeTagDb) {
- mTestLooper.dispatchAll();
+ private String dumpLog(WakeLockLog log, boolean includeTagDb) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
log.dump(pw, includeTagDb);
@@ -244,11 +228,6 @@
}
@Override
- public Looper getLooper() {
- return mTestLooper.getLooper();
- }
-
- @Override
public int getTagDatabaseSize() {
return mTagDatabaseSize;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c527e66..6b7fc2f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3682,6 +3682,13 @@
"emergency_number_prefix_string_array";
/**
+ * Indicates whether carrier treats "*67" or "*82" as a temporary mode CLIR.
+ * @hide
+ */
+ public static final String KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL =
+ "carrier_supports_caller_id_vertical_service_codes_bool";
+
+ /**
* Smart forwarding config. Smart forwarding is a feature to configure call forwarding to a
* different SIM in the device when one SIM is not reachable. The config here specifies a smart
* forwarding component that will launch UI for changing the configuration. An empty string
@@ -5496,6 +5503,7 @@
1 /* Roaming Indicator Off */
});
sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
+ sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, false);
sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true);