Merge TQ2A.230305.008

Bug: 264720040
Merged-In: I56c2fc14f906cdad80181ab577e2ebc276c151c1
Change-Id: Ib6d03795b3c3419f549d5f834cfdb6b409eb9e28
This commit is contained in:
Xin Li
2023-02-14 16:11:39 -08:00
51 changed files with 899 additions and 554 deletions

View File

@@ -16,9 +16,7 @@
package android.preference;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.NotificationManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
@@ -37,7 +35,6 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.preference.VolumePreference.VolumeStore;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.System;
@@ -47,7 +44,6 @@ import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.os.SomeArgs;
import java.util.concurrent.TimeUnit;
@@ -119,6 +115,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private final int mMaxStreamVolume;
private boolean mAffectedByRingerMode;
private boolean mNotificationOrRing;
private final boolean mNotifAliasRing;
private final Receiver mReceiver = new Receiver();
private Handler mHandler;
@@ -161,7 +158,6 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
this(context, streamType, defaultUri, callback, true /* playSample */);
}
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public SeekBarVolumizer(
Context context,
int streamType,
@@ -184,6 +180,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
}
mNotifAliasRing = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
mZenMode = mNotificationManager.getZenMode();
if (hasAudioProductStrategies()) {
@@ -290,9 +288,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
* so that when user attempts to slide the notification seekbar out of vibrate the
* seekbar doesn't wrongly snap back to 0 when the streams aren't aliased
*/
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
|| mStreamType == AudioManager.STREAM_RING
if (mNotifAliasRing || mStreamType == AudioManager.STREAM_RING
|| (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
mSeekBar.setProgress(0, true);
}
@@ -369,9 +365,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
// set the time of stop volume
if ((mStreamType == AudioManager.STREAM_VOICE_CALL
|| mStreamType == AudioManager.STREAM_RING
|| (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
&& mStreamType == AudioManager.STREAM_NOTIFICATION)
|| (!mNotifAliasRing && mStreamType == AudioManager.STREAM_NOTIFICATION)
|| mStreamType == AudioManager.STREAM_ALARM)) {
sStopVolumeTime = java.lang.System.currentTimeMillis();
}
@@ -650,10 +644,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
}
private void updateVolumeSlider(int streamType, int streamValue) {
final boolean streamMatch = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false)
&& mNotificationOrRing ? isNotificationOrRing(streamType) :
streamType == mStreamType;
final boolean streamMatch = mNotifAliasRing && mNotificationOrRing
? isNotificationOrRing(streamType) : streamType == mStreamType;
if (mSeekBar != null && streamMatch && streamValue != -1) {
final boolean muted = mAudioManager.isStreamMute(mStreamType)
|| streamValue == 0;

View File

@@ -561,11 +561,6 @@ public final class SystemUiDeviceConfigFlags {
public static final String TASK_MANAGER_SHOW_USER_VISIBLE_JOBS =
"task_manager_show_user_visible_jobs";
/**
* (boolean) Whether to show notification volume control slider separate from ring.
*/
public static final String VOLUME_SEPARATE_NOTIFICATION = "volume_separate_notification";
/**
* (boolean) Whether the clipboard overlay is enabled.
*/

View File

@@ -2018,6 +2018,10 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
<!-- Flag indicating whether notification and ringtone volumes
are controlled together (aliasing is true) or not. -->
<bool name="config_alias_ring_notif_stream_types">true</bool>
<!-- The number of volume steps for the notification stream -->
<integer name="config_audio_notif_vol_steps">7</integer>

View File

@@ -278,6 +278,7 @@
<java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
<java-symbol type="bool" name="config_alias_ring_notif_stream_types" />
<java-symbol type="integer" name="config_audio_notif_vol_default" />
<java-symbol type="integer" name="config_audio_notif_vol_steps" />
<java-symbol type="integer" name="config_audio_ring_vol_default" />

View File

@@ -791,13 +791,13 @@ private class AnimatedDialog(
// Move the drawing of the source in the overlay of this dialog, then animate. We trigger a
// one-off synchronization to make sure that this is done in sync between the two different
// windows.
controller.startDrawingInOverlayOf(decorView)
synchronizeNextDraw(
then = {
isSourceDrawnInDialog = true
maybeStartLaunchAnimation()
}
)
controller.startDrawingInOverlayOf(decorView)
}
/**

View File

@@ -195,14 +195,16 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
backgroundDrawable = WrappedDrawable(background)
backgroundView?.background = backgroundDrawable
// Delay the calls to `ghostedView.setVisibility()` during the animation. This must be
// called before `GhostView.addGhost()` is called because the latter will change the
// *transition* visibility, which won't be blocked and will affect the normal View
// visibility that is saved by `setShouldBlockVisibilityChanges()` for a later restoration.
(ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
// Create a ghost of the view that will be moving and fading out. This allows to fade out
// the content before fading out the background.
ghostView = GhostView.addGhost(ghostedView, launchContainer)
// The ghost was just created, so ghostedView is currently invisible. We need to make sure
// that it stays invisible as long as we are animating.
(ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
@@ -297,14 +299,19 @@ open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
(ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
launchContainerOverlay.remove(backgroundView)
// Make sure that the view is considered VISIBLE by accessibility by first making it
// INVISIBLE then VISIBLE (see b/204944038#comment17 for more info).
ghostedView.visibility = View.INVISIBLE
ghostedView.visibility = View.VISIBLE
ghostedView.invalidate()
if (ghostedView is LaunchableView) {
// Restore the ghosted view visibility.
ghostedView.setShouldBlockVisibilityChanges(false)
} else {
// Make the ghosted view visible. We ensure that the view is considered VISIBLE by
// accessibility by first making it INVISIBLE then VISIBLE (see b/204944038#comment17
// for more info).
ghostedView.visibility = View.INVISIBLE
ghostedView.visibility = View.VISIBLE
ghostedView.invalidate()
}
}
companion object {

View File

@@ -21,15 +21,19 @@ import android.view.View
/** A view that can expand/launch into an app or a dialog. */
interface LaunchableView {
/**
* Set whether this view should block/postpone all visibility changes. This ensures that this
* view:
* Set whether this view should block/postpone all calls to [View.setVisibility]. This ensures
* that this view:
* - remains invisible during the launch animation given that it is ghosted and already drawn
* somewhere else.
* - remains invisible as long as a dialog expanded from it is shown.
* - restores its expected visibility once the dialog expanded from it is dismissed.
*
* Note that when this is set to true, both the [normal][android.view.View.setVisibility] and
* [transition][android.view.View.setTransitionVisibility] visibility changes must be blocked.
* When `setShouldBlockVisibilityChanges(false)` is called, then visibility of the View should
* be restored to its expected value, i.e. it should have the visibility of the last call to
* `View.setVisibility()` that was made after `setShouldBlockVisibilityChanges(true)`, if any,
* or the original view visibility otherwise.
*
* Note that calls to [View.setTransitionVisibility] shouldn't be blocked.
*
* @param block whether we should block/postpone all calls to `setVisibility` and
* `setTransitionVisibility`.
@@ -46,27 +50,31 @@ class LaunchableViewDelegate(
* super.setVisibility(visibility).
*/
private val superSetVisibility: (Int) -> Unit,
/**
* The lambda that should set the actual transition visibility of [view], usually by calling
* super.setTransitionVisibility(visibility).
*/
private val superSetTransitionVisibility: (Int) -> Unit,
) {
) : LaunchableView {
private var blockVisibilityChanges = false
private var lastVisibility = view.visibility
/** Call this when [LaunchableView.setShouldBlockVisibilityChanges] is called. */
fun setShouldBlockVisibilityChanges(block: Boolean) {
override fun setShouldBlockVisibilityChanges(block: Boolean) {
if (block == blockVisibilityChanges) {
return
}
blockVisibilityChanges = block
if (block) {
// Save the current visibility for later.
lastVisibility = view.visibility
} else {
superSetVisibility(lastVisibility)
// Restore the visibility. To avoid accessibility issues, we change the visibility twice
// which makes sure that we trigger a visibility flag change (see b/204944038#comment17
// for more info).
if (lastVisibility == View.VISIBLE) {
superSetVisibility(View.INVISIBLE)
superSetVisibility(View.VISIBLE)
} else {
superSetVisibility(View.VISIBLE)
superSetVisibility(lastVisibility)
}
}
}
@@ -79,16 +87,4 @@ class LaunchableViewDelegate(
superSetVisibility(visibility)
}
/** Call this when [View.setTransitionVisibility] is called. */
fun setTransitionVisibility(visibility: Int) {
if (blockVisibilityChanges) {
// View.setTransitionVisibility just sets the visibility flag, so we don't have to save
// the transition visibility separately from the normal visibility.
lastVisibility = visibility
return
}
superSetTransitionVisibility(visibility)
}
}

View File

@@ -34,23 +34,29 @@ internal constructor(
override val sourceIdentity: Any = source
override fun startDrawingInOverlayOf(viewGroup: ViewGroup) {
// Delay the calls to `source.setVisibility()` during the animation. This must be called
// before `GhostView.addGhost()` is called because the latter will change the *transition*
// visibility, which won't be blocked and will affect the normal View visibility that is
// saved by `setShouldBlockVisibilityChanges()` for a later restoration.
(source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
// Create a temporary ghost of the source (which will make it invisible) and add it
// to the host dialog.
GhostView.addGhost(source, viewGroup)
// The ghost of the source was just created, so the source is currently invisible.
// We need to make sure that it stays invisible as long as the dialog is shown or
// animating.
(source as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
}
override fun stopDrawingInOverlay() {
// Note: here we should remove the ghost from the overlay, but in practice this is
// already done by the launch controllers created below.
// already done by the launch controller created below.
// Make sure we allow the source to change its visibility again.
(source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
source.visibility = View.VISIBLE
if (source is LaunchableView) {
// Make sure we allow the source to change its visibility again and restore its previous
// value.
source.setShouldBlockVisibilityChanges(false)
} else {
// We made the source invisible earlier, so let's make it visible again.
source.visibility = View.VISIBLE
}
}
override fun createLaunchController(): LaunchAnimator.Controller {
@@ -67,10 +73,14 @@ internal constructor(
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
// We hide the source when the dialog is showing. We will make this view
// visible again when dismissing the dialog. This does nothing if the source
// implements [LaunchableView], as it's already INVISIBLE in that case.
source.visibility = View.INVISIBLE
// At this point the view visibility is restored by the delegate, so we delay the
// visibility changes again and make it invisible while the dialog is shown.
if (source is LaunchableView) {
source.setShouldBlockVisibilityChanges(true)
source.setTransitionVisibility(View.INVISIBLE)
} else {
source.visibility = View.INVISIBLE
}
}
}
}
@@ -90,13 +100,15 @@ internal constructor(
}
override fun onExitAnimationCancelled() {
// Make sure we allow the source to change its visibility again.
(source as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
// If the view is invisible it's probably because of us, so we make it visible
// again.
if (source.visibility == View.INVISIBLE) {
source.visibility = View.VISIBLE
if (source is LaunchableView) {
// Make sure we allow the source to change its visibility again.
source.setShouldBlockVisibilityChanges(false)
} else {
// If the view is invisible it's probably because of us, so we make it visible
// again.
if (source.visibility == View.INVISIBLE) {
source.visibility = View.VISIBLE
}
}
}

View File

@@ -1,26 +0,0 @@
<!--
Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
<path
android:pathData="M11,7V2H13V7ZM17.6,9.85 L16.2,8.4 19.75,4.85 21.15,6.3ZM6.4,9.85 L2.85,6.3 4.25,4.85 7.8,8.4ZM12,12Q14.95,12 17.812,13.188Q20.675,14.375 22.9,16.75Q23.2,17.05 23.2,17.45Q23.2,17.85 22.9,18.15L20.6,20.4Q20.325,20.675 19.963,20.7Q19.6,20.725 19.3,20.5L16.4,18.3Q16.2,18.15 16.1,17.95Q16,17.75 16,17.5V14.65Q15.05,14.35 14.05,14.175Q13.05,14 12,14Q10.95,14 9.95,14.175Q8.95,14.35 8,14.65V17.5Q8,17.75 7.9,17.95Q7.8,18.15 7.6,18.3L4.7,20.5Q4.4,20.725 4.038,20.7Q3.675,20.675 3.4,20.4L1.1,18.15Q0.8,17.85 0.8,17.45Q0.8,17.05 1.1,16.75Q3.3,14.375 6.175,13.188Q9.05,12 12,12ZM6,15.35Q5.275,15.725 4.6,16.212Q3.925,16.7 3.2,17.3L4.2,18.3L6,16.9ZM18,15.4V16.9L19.8,18.3L20.8,17.35Q20.075,16.7 19.4,16.225Q18.725,15.75 18,15.4ZM6,15.35Q6,15.35 6,15.35Q6,15.35 6,15.35ZM18,15.4Q18,15.4 18,15.4Q18,15.4 18,15.4Z"
android:fillColor="?android:attr/colorPrimary"/>
</vector>

View File

@@ -1,34 +0,0 @@
<!--
Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
<path
android:pathData="M0.8,4.2l8.1,8.1c-2.2,0.5 -5.2,1.6 -7.8,4.4c-0.4,0.4 -0.4,1 0,1.4l2.3,2.3c0.3,0.3 0.9,0.4 1.3,0.1l2.9,-2.2C7.8,18.1 8,17.8 8,17.5v-2.9c0.9,-0.3 1.7,-0.5 2.7,-0.6l8.5,8.5l1.4,-1.4L2.2,2.8L0.8,4.2z"
android:fillColor="?android:attr/colorPrimary"/>
<path
android:pathData="M11,2h2v5h-2z"
android:fillColor="?android:attr/colorPrimary"/>
<path
android:pathData="M21.2,6.3l-1.4,-1.4l-3.6,3.6l1.4,1.4C17.6,9.8 21,6.3 21.2,6.3z"
android:fillColor="?android:attr/colorPrimary"/>
<path
android:pathData="M22.9,16.7c-2.8,-3 -6.2,-4.1 -8.4,-4.5l7.2,7.2l1.3,-1.3C23.3,17.7 23.3,17.1 22.9,16.7z"
android:fillColor="?android:attr/colorPrimary"/>
</vector>

View File

@@ -1,25 +0,0 @@
<!--
~ Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/textColorPrimary"
android:autoMirrored="true">
<path android:fillColor="#FFFFFFFF"
android:pathData="M19.8,22.6 L16.775,19.575Q16.15,19.975 15.45,20.263Q14.75,20.55 14,20.725V18.675Q14.35,18.55 14.688,18.425Q15.025,18.3 15.325,18.125L12,14.8V20L7,15H3V9H6.2L1.4,4.2L2.8,2.8L21.2,21.2ZM19.6,16.8 L18.15,15.35Q18.575,14.575 18.788,13.725Q19,12.875 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,13.3 20.638,14.525Q20.275,15.75 19.6,16.8ZM16.25,13.45 L14,11.2V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,12.375 16.438,12.738Q16.375,13.1 16.25,13.45ZM12,9.2 L9.4,6.6 12,4ZM10,15.15V12.8L8.2,11H5V13H7.85ZM9.1,11.9Z"/>
</vector>

View File

@@ -1,25 +0,0 @@
<!--
~ Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/textColorPrimary"
android:autoMirrored="true">
<path android:fillColor="#FFFFFFFF"
android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,13.275 15.838,14.362Q15.175,15.45 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/>
</vector>

View File

@@ -148,9 +148,4 @@
<include layout="@layout/ongoing_privacy_chip"/>
</FrameLayout>
<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/space"
/>
</com.android.systemui.util.NoRemeasureMotionLayout>

View File

@@ -56,13 +56,9 @@
<Layout
android:layout_width="wrap_content"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constrainedWidth="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/space"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/carrier_group"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
</Constraint>
@@ -87,39 +83,27 @@
<Constraint
android:id="@+id/statusIcons">
<Layout
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintStart_toEndOf="@id/space"
app:layout_constraintWidth_default="wrap"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintBottom_toBottomOf="@id/date"
/>
</Constraint>
<Constraint
android:id="@+id/batteryRemainingIcon">
<Layout
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintWidth_default="wrap"
app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintBottom_toBottomOf="@id/date"
/>
</Constraint>
<Constraint
android:id="@id/space">
<Layout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toStartOf="@id/statusIcons"
/>
</Constraint>
</ConstraintSet>

View File

@@ -28,7 +28,6 @@ class LaunchableImageView : ImageView, LaunchableView {
LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
constructor(context: Context?) : super(context)
@@ -53,8 +52,4 @@ class LaunchableImageView : ImageView, LaunchableView {
override fun setVisibility(visibility: Int) {
delegate.setVisibility(visibility)
}
override fun setTransitionVisibility(visibility: Int) {
delegate.setTransitionVisibility(visibility)
}
}

View File

@@ -28,7 +28,6 @@ class LaunchableLinearLayout : LinearLayout, LaunchableView {
LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
constructor(context: Context?) : super(context)
@@ -53,8 +52,4 @@ class LaunchableLinearLayout : LinearLayout, LaunchableView {
override fun setVisibility(visibility: Int) {
delegate.setVisibility(visibility)
}
override fun setTransitionVisibility(visibility: Int) {
delegate.setTransitionVisibility(visibility)
}
}

View File

@@ -36,7 +36,6 @@ public interface QSHost {
void removeCallback(Callback callback);
void removeTile(String tileSpec);
void removeTiles(Collection<String> specs);
void unmarkTileAsAutoAdded(String tileSpec);
int indexOf(String tileSpec);

View File

@@ -427,11 +427,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
}
@Override
public void unmarkTileAsAutoAdded(String spec) {
if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
}
/**
* Add a tile to the end
*

View File

@@ -24,6 +24,7 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toolbar;
@@ -74,8 +75,8 @@ public class QSCustomizer extends LinearLayout {
toolbar.setNavigationIcon(
getResources().getDrawable(value.resourceId, mContext.getTheme()));
toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
mContext.getString(com.android.internal.R.string.reset));
toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, com.android.internal.R.string.reset)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
toolbar.setTitle(R.string.qs_edit);
mRecyclerView = findViewById(android.R.id.list);
mTransparentView = findViewById(R.id.customizer_transparent_view);

View File

@@ -132,7 +132,7 @@ public class TileServices extends IQSService.Stub {
final String slot = tile.getComponent().getClassName();
// TileServices doesn't know how to add more than 1 icon per slot, so remove all
mMainHandler.post(() -> mHost.getIconController()
.removeAllIconsForSlot(slot));
.removeAllIconsForExternalSlot(slot));
}
}

View File

@@ -219,9 +219,9 @@ object FooterActionsViewBinder {
// Small button with the number only.
foregroundServicesWithTextView.isVisible = false
foregroundServicesWithNumberView.visibility = View.VISIBLE
foregroundServicesWithNumberView.isVisible = true
foregroundServicesWithNumberView.setOnClickListener {
foregroundServices.onClick(Expandable.fromView(foregroundServicesWithTextView))
foregroundServices.onClick(Expandable.fromView(foregroundServicesWithNumberView))
}
foregroundServicesWithNumberHolder.number.text = foregroundServicesCount.toString()
foregroundServicesWithNumberHolder.number.contentDescription = foregroundServices.text

View File

@@ -145,7 +145,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
private val launchableViewDelegate = LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
superSetTransitionVisibility = { super.setTransitionVisibility(it) },
)
private var lastDisabledByPolicy = false
@@ -362,10 +361,6 @@ open class QSTileViewImpl @JvmOverloads constructor(
launchableViewDelegate.setVisibility(visibility)
}
override fun setTransitionVisibility(visibility: Int) {
launchableViewDelegate.setTransitionVisibility(visibility)
}
// Accessibility
override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {

View File

@@ -101,7 +101,6 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
@MainThread
public void onManagedProfileRemoved() {
mHost.removeTile(getTileSpec());
mHost.unmarkTileAsAutoAdded(getTileSpec());
}
@Override

View File

@@ -112,7 +112,7 @@ class UserTrackerImpl internal constructor(
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
}
@@ -129,6 +129,7 @@ class UserTrackerImpl internal constructor(
Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_ADDED,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> {
handleProfilesChanged()

View File

@@ -175,9 +175,10 @@ class LargeScreenShadeHeaderController @Inject constructor(
*/
var shadeExpandedFraction = -1f
set(value) {
if (visible && field != value) {
if (field != value) {
header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
updateVisibility()
}
}
@@ -331,6 +332,9 @@ class LargeScreenShadeHeaderController @Inject constructor(
.setDuration(duration)
.alpha(if (show) 0f else 1f)
.setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
.setUpdateListener {
updateVisibility()
}
.start()
}
@@ -414,7 +418,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
private fun updateVisibility() {
val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) {
View.GONE
} else if (qsVisible) {
} else if (qsVisible && header.alpha > 0f) {
View.VISIBLE
} else {
View.INVISIBLE

View File

@@ -36,10 +36,6 @@ public class AlphaOptimizedFrameLayout extends FrameLayout implements Launchable
visibility -> {
super.setVisibility(visibility);
return Unit.INSTANCE;
},
visibility -> {
super.setTransitionVisibility(visibility);
return Unit.INSTANCE;
});
public AlphaOptimizedFrameLayout(Context context) {
@@ -73,9 +69,4 @@ public class AlphaOptimizedFrameLayout extends FrameLayout implements Launchable
public void setVisibility(int visibility) {
mLaunchableViewDelegate.setVisibility(visibility);
}
@Override
public void setTransitionVisibility(int visibility) {
mLaunchableViewDelegate.setTransitionVisibility(visibility);
}
}

View File

@@ -1431,6 +1431,22 @@ public class NotificationChildrenContainer extends ViewGroup
@Override
public void applyRoundnessAndInvalidate() {
boolean last = true;
if (mUseRoundnessSourceTypes) {
if (mNotificationHeaderWrapper != null) {
mNotificationHeaderWrapper.requestTopRoundness(
/* value = */ getTopRoundness(),
/* sourceType = */ FROM_PARENT,
/* animate = */ false
);
}
if (mNotificationHeaderWrapperLowPriority != null) {
mNotificationHeaderWrapperLowPriority.requestTopRoundness(
/* value = */ getTopRoundness(),
/* sourceType = */ FROM_PARENT,
/* animate = */ false
);
}
}
for (int i = mAttachedChildren.size() - 1; i >= 0; i--) {
ExpandableNotificationRow child = mAttachedChildren.get(i);
if (child.getVisibility() == View.GONE) {

View File

@@ -5193,7 +5193,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
println(pw, "intrinsicPadding", mIntrinsicPadding);
println(pw, "topPadding", mTopPadding);
println(pw, "bottomPadding", mBottomPadding);
mNotificationStackSizeCalculator.dump(pw, args);
});
pw.println();
pw.println("Contents:");

View File

@@ -30,7 +30,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.Compile
import com.android.systemui.util.children
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
@@ -54,8 +53,6 @@ constructor(
@Main private val resources: Resources
) {
private lateinit var lastComputeHeightLog : String
/**
* Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow shelf.
* If there are exactly 1 + mMaxKeyguardNotifications, and they fit in the available space
@@ -117,9 +114,7 @@ constructor(
shelfIntrinsicHeight: Float
): Int {
log { "\n" }
val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
/* computeHeight= */ false)
val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
var maxNotifications =
stackHeightSequence.lastIndexWhile { heightResult ->
@@ -162,21 +157,18 @@ constructor(
shelfIntrinsicHeight: Float
): Float {
log { "\n" }
lastComputeHeightLog = ""
val heightPerMaxNotifications =
computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
/* computeHeight= */ true)
computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
val (notificationsHeight, shelfHeightWithSpaceBefore) =
heightPerMaxNotifications.elementAtOrElse(maxNotifications) {
heightPerMaxNotifications.last() // Height with all notifications visible.
}
lastComputeHeightLog += "\ncomputeHeight(maxNotifications=$maxNotifications," +
log {
"computeHeight(maxNotifications=$maxNotifications," +
"shelfIntrinsicHeight=$shelfIntrinsicHeight) -> " +
"${notificationsHeight + shelfHeightWithSpaceBefore}" +
" = ($notificationsHeight + $shelfHeightWithSpaceBefore)"
log {
lastComputeHeightLog
}
return notificationsHeight + shelfHeightWithSpaceBefore
}
@@ -192,8 +184,7 @@ constructor(
private fun computeHeightPerNotificationLimit(
stack: NotificationStackScrollLayout,
shelfHeight: Float,
computeHeight: Boolean
shelfHeight: Float
): Sequence<StackHeight> = sequence {
log { "computeHeightPerNotificationLimit" }
@@ -222,14 +213,9 @@ constructor(
currentIndex = firstViewInShelfIndex)
spaceBeforeShelf + shelfHeight
}
val currentLog = "computeHeight | i=$i notificationsHeight=$notifications " +
"shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
if (computeHeight) {
lastComputeHeightLog += "\n" + currentLog
}
log {
currentLog
"i=$i notificationsHeight=$notifications " +
"shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
}
yield(
StackHeight(
@@ -274,10 +260,6 @@ constructor(
return size
}
fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("NotificationStackSizeCalculator lastComputeHeightLog = $lastComputeHeightLog")
}
private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean {
if (visibility == GONE || hasNoContentHeight()) return false
if (onLockscreen) {

View File

@@ -154,9 +154,7 @@ public class AutoTileManager implements UserAwareController {
if (!mAutoTracker.isAdded(SAVER)) {
mDataSaverController.addCallback(mDataSaverListener);
}
if (!mAutoTracker.isAdded(WORK)) {
mManagedProfileController.addCallback(mProfileCallback);
}
mManagedProfileController.addCallback(mProfileCallback);
if (!mAutoTracker.isAdded(NIGHT)
&& ColorDisplayManager.isNightDisplayAvailable(mContext)) {
mNightDisplayListener.setCallback(mNightDisplayCallback);
@@ -275,18 +273,18 @@ public class AutoTileManager implements UserAwareController {
return mCurrentUser.getIdentifier();
}
public void unmarkTileAsAutoAdded(String tabSpec) {
mAutoTracker.setTileRemoved(tabSpec);
}
private final ManagedProfileController.Callback mProfileCallback =
new ManagedProfileController.Callback() {
@Override
public void onManagedProfileChanged() {
if (mAutoTracker.isAdded(WORK)) return;
if (mManagedProfileController.hasActiveProfile()) {
if (mAutoTracker.isAdded(WORK)) return;
mHost.addTile(WORK);
mAutoTracker.setTileAdded(WORK);
} else {
if (!mAutoTracker.isAdded(WORK)) return;
mHost.removeTile(WORK);
mAutoTracker.setTileRemoved(WORK);
}
}
@@ -429,7 +427,7 @@ public class AutoTileManager implements UserAwareController {
initSafetyTile();
} else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
mHost.removeTile(mSafetySpec);
mHost.unmarkTileAsAutoAdded(mSafetySpec);
mAutoTracker.setTileRemoved(mSafetySpec);
}
}
};

View File

@@ -79,12 +79,30 @@ public interface StatusBarIconController {
/** Refresh the state of an IconManager by recreating the views */
void refreshIconGroup(IconManager iconManager);
/** */
/**
* Adds or updates an icon for a given slot for a **tile service icon**.
*
* TODO(b/265307726): Merge with {@link #setIcon(String, StatusBarIcon)} or make this method
* much more clearly distinct from that method.
*/
void setExternalIcon(String slot);
/** */
/**
* Adds or updates an icon for the given slot for **internal system icons**.
*
* TODO(b/265307726): Rename to `setInternalIcon`, or merge this appropriately with the
* {@link #setIcon(String, StatusBarIcon)} method.
*/
void setIcon(String slot, int resourceId, CharSequence contentDescription);
/** */
/**
* Adds or updates an icon for the given slot for an **externally-provided icon**.
*
* TODO(b/265307726): Rename to `setExternalIcon` or something similar.
*/
void setIcon(String slot, StatusBarIcon icon);
/** */
void setWifiIcon(String slot, WifiIconState state);
@@ -133,9 +151,17 @@ public interface StatusBarIconController {
* TAG_PRIMARY to refer to the first icon at a given slot.
*/
void removeIcon(String slot, int tag);
/** */
void removeAllIconsForSlot(String slot);
/**
* Removes all the icons for the given slot.
*
* Only use this for icons that have come from **an external process**.
*/
void removeAllIconsForExternalSlot(String slot);
// TODO: See if we can rename this tunable name.
String ICON_HIDE_LIST = "icon_blacklist";

View File

@@ -28,6 +28,8 @@ import android.util.ArraySet;
import android.util.Log;
import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -63,6 +65,10 @@ public class StatusBarIconControllerImpl implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode {
private static final String TAG = "StatusBarIconController";
// Use this suffix to prevent external icon slot names from unintentionally overriding our
// internal, system-level slot names. See b/255428281.
@VisibleForTesting
protected static final String EXTERNAL_SLOT_SUFFIX = "__external";
private final StatusBarIconList mStatusBarIconList;
private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
@@ -346,21 +352,26 @@ public class StatusBarIconControllerImpl implements Tunable,
@Override
public void setExternalIcon(String slot) {
int viewIndex = mStatusBarIconList.getViewIndex(slot, 0);
String slotName = createExternalSlotName(slot);
int viewIndex = mStatusBarIconList.getViewIndex(slotName, 0);
int height = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_drawing_size);
mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
}
//TODO: remove this (used in command queue and for 3rd party tiles?)
// Override for *both* CommandQueue.Callbacks AND StatusBarIconController.
// TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
// differentiate between those callback methods and StatusBarIconController methods.
@Override
public void setIcon(String slot, StatusBarIcon icon) {
String slotName = createExternalSlotName(slot);
if (icon == null) {
removeAllIconsForSlot(slot);
removeAllIconsForSlot(slotName);
return;
}
StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
setIcon(slot, holder);
setIcon(slotName, holder);
}
private void setIcon(String slot, @NonNull StatusBarIconHolder holder) {
@@ -406,10 +417,12 @@ public class StatusBarIconControllerImpl implements Tunable,
}
}
/** */
// CommandQueue.Callbacks override
// TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
// differentiate between those callback methods and StatusBarIconController methods.
@Override
public void removeIcon(String slot) {
removeAllIconsForSlot(slot);
removeAllIconsForExternalSlot(slot);
}
/** */
@@ -423,6 +436,11 @@ public class StatusBarIconControllerImpl implements Tunable,
mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
}
@Override
public void removeAllIconsForExternalSlot(String slotName) {
removeAllIconsForSlot(createExternalSlotName(slotName));
}
/** */
@Override
public void removeAllIconsForSlot(String slotName) {
@@ -506,4 +524,12 @@ public class StatusBarIconControllerImpl implements Tunable,
public void onDensityOrFontScaleChanged() {
refreshIconGroups();
}
private String createExternalSlotName(String slot) {
if (slot.endsWith(EXTERNAL_SLOT_SUFFIX)) {
return slot;
} else {
return slot + EXTERNAL_SLOT_SUFFIX;
}
}
}

View File

@@ -72,7 +72,6 @@ import android.os.Message;
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
@@ -109,8 +108,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.RotationPolicy;
@@ -130,15 +127,11 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.RoundedCornerProgressDrawable;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -195,9 +188,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private ViewGroup mDialogRowsView;
private ViewGroup mRinger;
private DeviceConfigProxy mDeviceConfigProxy;
private Executor mExecutor;
/**
* Container for the top part of the dialog, which contains the ringer, the ringer drawer, the
* volume rows, and the ellipsis button. This does not include the live caption button.
@@ -286,13 +276,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private BackgroundBlurDrawable mDialogRowsViewBackground;
private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mSeparateNotification;
@VisibleForTesting
int mVolumeRingerIconDrawableId;
@VisibleForTesting
int mVolumeRingerMuteIconDrawableId;
public VolumeDialogImpl(
Context context,
VolumeDialogController volumeDialogController,
@@ -303,8 +286,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
InteractionJankMonitor interactionJankMonitor,
DeviceConfigProxy deviceConfigProxy,
Executor executor,
DumpManager dumpManager) {
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
@@ -347,50 +328,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
}
initDimens();
mDeviceConfigProxy = deviceConfigProxy;
mExecutor = executor;
mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
updateRingerModeIconSet();
}
/**
* If ringer and notification are the same stream (T and earlier), use notification-like bell
* icon set.
* If ringer and notification are separated, then use generic speaker icons.
*/
private void updateRingerModeIconSet() {
if (mSeparateNotification) {
mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on;
mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute;
} else {
mVolumeRingerIconDrawableId = R.drawable.ic_volume_ringer;
mVolumeRingerMuteIconDrawableId = R.drawable.ic_volume_ringer_mute;
}
if (mRingerDrawerMuteIcon != null) {
mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
}
if (mRingerDrawerNormalIcon != null) {
mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
}
}
/**
* Change icon for ring stream (not ringer mode icon)
*/
private void updateRingRowIcon() {
Optional<VolumeRow> volumeRow = mRows.stream().filter(row -> row.stream == STREAM_RING)
.findFirst();
if (volumeRow.isPresent()) {
VolumeRow volRow = volumeRow.get();
volRow.iconRes = mSeparateNotification ? R.drawable.ic_ring_volume
: R.drawable.ic_volume_ringer;
volRow.iconMuteRes = mSeparateNotification ? R.drawable.ic_ring_volume_off
: R.drawable.ic_volume_ringer_mute;
volRow.setIcon(volRow.iconRes, mContext.getTheme());
}
}
@Override
@@ -407,9 +344,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mController.getState();
mConfigurationController.addCallback(this);
mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
mExecutor, this::onDeviceConfigChange);
}
@Override
@@ -417,24 +351,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mController.removeCallback(mControllerCallbackH);
mHandler.removeCallbacksAndMessages(null);
mConfigurationController.removeCallback(this);
mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
}
/**
* Update ringer mode icon based on the config
*/
private void onDeviceConfigChange(DeviceConfig.Properties properties) {
Set<String> changeSet = properties.getKeyset();
if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
boolean newVal = properties.getBoolean(
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
if (newVal != mSeparateNotification) {
mSeparateNotification = newVal;
updateRingerModeIconSet();
updateRingRowIcon();
}
}
}
@Override
@@ -643,8 +559,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
updateRingerModeIconSet();
setupRingerDrawer();
mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
@@ -668,14 +582,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
if (!AudioSystem.isSingleVolume(mContext)) {
if (mSeparateNotification) {
addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
R.drawable.ic_ring_volume_off, true, false);
} else {
addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer,
R.drawable.ic_volume_ringer, true, false);
}
addRow(AudioManager.STREAM_RING,
R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
addRow(STREAM_ALARM,
R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false);
addRow(AudioManager.STREAM_VOICE_CALL,
@@ -1634,8 +1542,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
break;
case AudioManager.RINGER_MODE_SILENT:
mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
mRingerIcon.setTag(Events.ICON_STATE_MUTE);
addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
mContext.getString(R.string.volume_ringer_hint_unmute));
@@ -1644,14 +1552,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
default:
boolean muted = (mAutomute && ss.level == 0) || ss.muted;
if (!isZenMuted && muted) {
mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
mContext.getString(R.string.volume_ringer_hint_unmute));
mRingerIcon.setTag(Events.ICON_STATE_MUTE);
} else {
mRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
mSelectedRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
if (mController.hasVibrator()) {
addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
mContext.getString(R.string.volume_ringer_hint_vibrate));

View File

@@ -20,7 +20,6 @@ import android.content.Context;
import android.media.AudioManager;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
@@ -29,14 +28,11 @@ import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumePanelFactory;
import java.util.concurrent.Executor;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -61,8 +57,6 @@ public interface VolumeModule {
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
InteractionJankMonitor interactionJankMonitor,
DeviceConfigProxy deviceConfigProxy,
@Main Executor executor,
DumpManager dumpManager) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
@@ -74,8 +68,6 @@ public interface VolumeModule {
volumePanelFactory,
activityStarter,
interactionJankMonitor,
deviceConfigProxy,
executor,
dumpManager);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);

View File

@@ -12,11 +12,13 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.policy.DecorView
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNotNull
@@ -205,25 +207,74 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
}
@Test
fun testAnimationDoesNotChangeLaunchableViewVisibility_viewVisible() {
val touchSurface = createTouchSurface()
// View is VISIBLE when starting the animation.
runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.VISIBLE }
// View is invisible while the dialog is shown.
val dialog = showDialogFromView(touchSurface)
assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
// View is visible again when the dialog is dismissed.
runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
assertThat(touchSurface.visibility).isEqualTo(View.VISIBLE)
}
@Test
fun testAnimationDoesNotChangeLaunchableViewVisibility_viewInvisible() {
val touchSurface = createTouchSurface()
// View is INVISIBLE when starting the animation.
runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.INVISIBLE }
// View is INVISIBLE while the dialog is shown.
val dialog = showDialogFromView(touchSurface)
assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
// View is invisible like it was before showing the dialog.
runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
}
@Test
fun testAnimationDoesNotChangeLaunchableViewVisibility_viewVisibleThenGone() {
val touchSurface = createTouchSurface()
// View is VISIBLE when starting the animation.
runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.VISIBLE }
// View is INVISIBLE while the dialog is shown.
val dialog = showDialogFromView(touchSurface)
assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
// Some external call makes the View GONE. It remains INVISIBLE while the dialog is shown,
// as all visibility changes should be blocked.
runOnMainThreadAndWaitForIdleSync { touchSurface.visibility = View.GONE }
assertThat(touchSurface.visibility).isEqualTo(View.INVISIBLE)
// View is restored to GONE once the dialog is dismissed.
runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
assertThat(touchSurface.visibility).isEqualTo(View.GONE)
}
private fun createAndShowDialog(
animator: DialogLaunchAnimator = dialogLaunchAnimator,
): TestDialog {
val touchSurface = createTouchSurface()
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
animator.showFromView(dialog, touchSurface)
dialog
}
return showDialogFromView(touchSurface, animator)
}
private fun createTouchSurface(): View {
return runOnMainThreadAndWaitForIdleSync {
val touchSurfaceRoot = LinearLayout(context)
val touchSurface = View(context)
val touchSurface = TouchSurfaceView(context)
touchSurfaceRoot.addView(touchSurface)
// We need to attach the root to the window manager otherwise the exit animation will
// be skipped
// be skipped.
ViewUtils.attachView(touchSurfaceRoot)
attachedViews.add(touchSurfaceRoot)
@@ -231,6 +282,17 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
}
}
private fun showDialogFromView(
touchSurface: View,
animator: DialogLaunchAnimator = dialogLaunchAnimator,
): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
animator.showFromView(dialog, touchSurface)
dialog
}
}
private fun createDialogAndShowFromDialog(animateFrom: Dialog): TestDialog {
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
@@ -248,6 +310,22 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
return result
}
private class TouchSurfaceView(context: Context) : FrameLayout(context), LaunchableView {
private val delegate =
LaunchableViewDelegate(
this,
superSetVisibility = { super.setVisibility(it) },
)
override fun setShouldBlockVisibilityChanges(block: Boolean) {
delegate.setShouldBlockVisibilityChanges(block)
}
override fun setVisibility(visibility: Int) {
delegate.setVisibility(visibility)
}
}
private class TestDialog(context: Context) : Dialog(context) {
companion object {
const val DIALOG_WIDTH = 100

View File

@@ -0,0 +1,100 @@
package com.android.systemui.settings
import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
import androidx.concurrent.futures.DirectExecutor
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(Parameterized::class)
class UserTrackerImplReceiveTest : SysuiTestCase() {
companion object {
@JvmStatic
@Parameterized.Parameters
fun data(): Iterable<String> =
listOf(
Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_ADDED,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED
)
}
private val executor: Executor = DirectExecutor.INSTANCE
@Mock private lateinit var context: Context
@Mock private lateinit var userManager: UserManager
@Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
@Mock(stubOnly = true) private lateinit var handler: Handler
@Parameterized.Parameter lateinit var intentAction: String
@Mock private lateinit var callback: UserTracker.Callback
@Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
private lateinit var tracker: UserTrackerImpl
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(context.user).thenReturn(UserHandle.SYSTEM)
`when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
}
@Test
fun `calls callback and updates profiles when an intent received`() {
tracker.initialize(0)
tracker.addCallback(callback, executor)
val profileID = tracker.userId + 10
`when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
val id = invocation.getArgument<Int>(0)
val info = UserInfo(id, "", UserInfo.FLAG_FULL)
val infoProfile =
UserInfo(
id + 10,
"",
"",
UserInfo.FLAG_MANAGED_PROFILE,
UserManager.USER_TYPE_PROFILE_MANAGED
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
}
tracker.onReceive(context, Intent(intentAction))
verify(callback, times(0)).onUserChanged(anyInt(), any())
verify(callback, times(1)).onProfilesChanged(capture(captor))
assertThat(captor.value.map { it.id }).containsExactly(0, profileID)
}
}

View File

@@ -124,6 +124,16 @@ class UserTrackerImplTest : SysuiTestCase() {
verify(context).registerReceiverForAllUsers(
eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
assertThat(countActions()).isEqualTo(7)
assertThat(hasAction(Intent.ACTION_USER_SWITCHED)).isTrue()
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
}
}
@Test
@@ -279,37 +289,6 @@ class UserTrackerImplTest : SysuiTestCase() {
assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
}
@Test
fun testCallbackCalledOnProfileChanged() {
tracker.initialize(0)
val callback = TestCallback()
tracker.addCallback(callback, executor)
val profileID = tracker.userId + 10
`when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
val id = invocation.getArgument<Int>(0)
val info = UserInfo(id, "", UserInfo.FLAG_FULL)
val infoProfile = UserInfo(
id + 10,
"",
"",
UserInfo.FLAG_MANAGED_PROFILE,
UserManager.USER_TYPE_PROFILE_MANAGED
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
}
val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
.putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
tracker.onReceive(context, intent)
assertThat(callback.calledOnUserChanged).isEqualTo(0)
assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
}
@Test
fun testCallbackCalledOnUserInfoChanged() {
tracker.initialize(0)

View File

@@ -17,6 +17,7 @@
package com.android.systemui.shade
import android.testing.AndroidTestingRunner
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
@@ -92,12 +93,12 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0f)
assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd)
.isEqualTo(PARENT_ID)
assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias)
.isEqualTo(1f)
.isEqualTo(0.5f)
assertThat(getConstraint(R.id.privacy_container).layout.endToEnd)
.isEqualTo(R.id.end_guide)
@@ -331,10 +332,8 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
val views = mapOf(
R.id.clock to "clock",
R.id.date to "date",
R.id.statusIcons to "icons",
R.id.privacy_container to "privacy",
R.id.carrier_group to "carriers",
R.id.batteryRemainingIcon to "battery",
)
views.forEach { (id, name) ->
assertWithMessage("$name has 0 height in qqs")
@@ -352,11 +351,8 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() {
val views = mapOf(
R.id.clock to "clock",
R.id.date to "date",
R.id.statusIcons to "icons",
R.id.privacy_container to "privacy",
R.id.carrier_group to "carriers",
R.id.batteryRemainingIcon to "battery",
)
views.forEach { (id, name) ->
expect.withMessage("$name changes height")
@@ -369,8 +365,8 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
}
private fun Int.fromConstraint() = when (this) {
-1 -> "MATCH_PARENT"
-2 -> "WRAP_CONTENT"
ViewGroup.LayoutParams.MATCH_PARENT -> "MATCH_PARENT"
ViewGroup.LayoutParams.WRAP_CONTENT -> "WRAP_CONTENT"
else -> toString()
}

View File

@@ -182,6 +182,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
null
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(view.alpha).thenReturn(1f)
whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager)

View File

@@ -1,5 +1,6 @@
package com.android.systemui.shade
import android.animation.ValueAnimator
import android.app.StatusBarManager
import android.content.Context
import android.testing.AndroidTestingRunner
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -37,6 +39,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.mock
@@ -75,6 +78,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
var viewAlpha = 1f
private lateinit var mLargeScreenShadeHeaderController: LargeScreenShadeHeaderController
private lateinit var carrierIconSlots: List<String>
@@ -101,6 +105,13 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
null
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(view.setAlpha(anyFloat())).then {
viewAlpha = it.arguments[0] as Float
null
}
whenever(view.alpha).thenAnswer { _ -> viewAlpha }
whenever(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager)
@@ -154,6 +165,16 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
@Test
fun alphaChangesUpdateVisibility() {
makeShadeVisible()
mLargeScreenShadeHeaderController.shadeExpandedFraction = 0f
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
mLargeScreenShadeHeaderController.shadeExpandedFraction = 1f
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
}
@Test
fun singleCarrier_enablesCarrierIconsInStatusIcons() {
whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
@@ -238,6 +259,39 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
verify(animator).start()
}
@Test
fun testShadeExpanded_true_alpha_zero_invisible() {
view.alpha = 0f
mLargeScreenShadeHeaderController.largeScreenActive = true
mLargeScreenShadeHeaderController.qsVisible = true
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
}
@Test
fun animatorCallsUpdateVisibilityOnUpdate() {
val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
whenever(view.animate()).thenReturn(animator)
mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, 0L)
val updateCaptor = argumentCaptor<ValueAnimator.AnimatorUpdateListener>()
verify(animator).setUpdateListener(capture(updateCaptor))
mLargeScreenShadeHeaderController.largeScreenActive = true
mLargeScreenShadeHeaderController.qsVisible = true
view.alpha = 1f
updateCaptor.value.onAnimationUpdate(mock())
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
view.alpha = 0f
updateCaptor.value.onAnimationUpdate(mock())
assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
}
@Test
fun demoMode_attachDemoMode() {
val cb = argumentCaptor<DemoMode>()

View File

@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import org.junit.Assert;
import org.junit.Before;
@@ -216,4 +217,29 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
Assert.assertEquals(1f, mChildrenContainer.getBottomRoundness(), 0.001f);
Assert.assertEquals(1f, notificationRow.getBottomRoundness(), 0.001f);
}
@Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_header() {
mChildrenContainer.useRoundnessSourceTypes(true);
NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper();
Assert.assertEquals(0f, header.getTopRoundness(), 0.001f);
mChildrenContainer.requestTopRoundness(1f, SourceType.from(""), false);
Assert.assertEquals(1f, header.getTopRoundness(), 0.001f);
}
@Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_headerLowPriority() {
mChildrenContainer.useRoundnessSourceTypes(true);
mChildrenContainer.setIsLowPriority(true);
NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper();
Assert.assertEquals(0f, header.getTopRoundness(), 0.001f);
mChildrenContainer.requestTopRoundness(1f, SourceType.from(""), false);
Assert.assertEquals(1f, header.getTopRoundness(), 0.001f);
}
}

View File

@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn;
@@ -74,6 +75,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.stubbing.Answer;
import java.util.Collections;
import java.util.List;
@@ -115,8 +117,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
@Spy private PackageManager mPackageManager;
private final boolean mIsReduceBrightColorsAvailable = true;
private AutoTileManager mAutoTileManager;
private AutoTileManager mAutoTileManager; // under test
private SecureSettings mSecureSettings;
private ManagedProfileController.Callback mManagedProfileCallback;
@Before
public void setUp() throws Exception {
@@ -303,7 +307,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
@@ -503,6 +507,40 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(mQsTileHost, times(2)).addTile(safetyComponent, true);
}
@Test
public void managedProfileAdded_tileAdded() {
when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
mAutoTileManager = createAutoTileManager(mContext);
Mockito.doAnswer((Answer<Object>) invocation -> {
mManagedProfileCallback = invocation.getArgument(0);
return null;
}).when(mManagedProfileController).addCallback(any());
mAutoTileManager.init();
when(mManagedProfileController.hasActiveProfile()).thenReturn(true);
mManagedProfileCallback.onManagedProfileChanged();
verify(mQsTileHost, times(1)).addTile(eq("work"));
verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
}
@Test
public void managedProfileRemoved_tileRemoved() {
when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(true);
mAutoTileManager = createAutoTileManager(mContext);
Mockito.doAnswer((Answer<Object>) invocation -> {
mManagedProfileCallback = invocation.getArgument(0);
return null;
}).when(mManagedProfileController).addCallback(any());
mAutoTileManager.init();
when(mManagedProfileController.hasActiveProfile()).thenReturn(false);
mManagedProfileCallback.onManagedProfileChanged();
verify(mQsTileHost, times(1)).removeTile(eq("work"));
verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
}
@Test
public void testEmptyArray_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(

View File

@@ -0,0 +1,309 @@
/*
* Copyright (C) 2023 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.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl.EXTERNAL_SLOT_SUFFIX
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.verify
@SmallTest
class StatusBarIconControllerImplTest : SysuiTestCase() {
private lateinit var underTest: StatusBarIconControllerImpl
private lateinit var iconList: StatusBarIconList
private val iconGroup: StatusBarIconController.IconManager = mock()
@Before
fun setUp() {
iconList = StatusBarIconList(arrayOf())
underTest =
StatusBarIconControllerImpl(
context,
mock(),
mock(),
mock(),
mock(),
mock(),
iconList,
mock(),
)
underTest.addIconGroup(iconGroup)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_bothDisplayed() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
val externalIcon =
StatusBarIcon(
"external.package",
UserHandle.ALL,
/* iconId= */ 2,
/* iconLevel= */ 0,
/* number= */ 0,
"contentDescription",
)
underTest.setIcon(slotName, externalIcon)
assertThat(iconList.slots).hasSize(2)
// Whichever was added last comes first
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveIcon_internalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
underTest.setIcon(slotName, createExternalIcon())
// WHEN the external icon is removed via #removeIcon
underTest.removeIcon(slotName)
// THEN the external icon is removed but the internal icon remains
// Note: [StatusBarIconList] never removes slots from its list, it just sets the holder for
// the slot to null when an icon is removed.
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
assertThat(iconList.slots[0].hasIconsInSlot()).isFalse() // Indicates removal
assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
verify(iconGroup).onRemoveIcon(0)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveAll_internalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
underTest.setIcon(slotName, createExternalIcon())
// WHEN the external icon is removed via #removeAllIconsForExternalSlot
underTest.removeAllIconsForExternalSlot(slotName)
// THEN the external icon is removed but the internal icon remains
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
assertThat(iconList.slots[0].hasIconsInSlot()).isFalse() // Indicates removal
assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
verify(iconGroup).onRemoveIcon(0)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_externalRemoved_viaSetNull_internalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
underTest.setIcon(slotName, createExternalIcon())
// WHEN the external icon is removed via a #setIcon(null)
underTest.setIcon(slotName, /* icon= */ null)
// THEN the external icon is removed but the internal icon remains
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
assertThat(iconList.slots[0].hasIconsInSlot()).isFalse() // Indicates removal
assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
verify(iconGroup).onRemoveIcon(0)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_internalRemoved_viaRemove_externalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
underTest.setIcon(slotName, createExternalIcon())
// WHEN the internal icon is removed via #removeIcon
underTest.removeIcon(slotName, /* tag= */ 0)
// THEN the external icon is removed but the internal icon remains
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
assertThat(iconList.slots[1].hasIconsInSlot()).isFalse() // Indicates removal
verify(iconGroup).onRemoveIcon(1)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_internalRemoved_viaRemoveAll_externalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
underTest.setIcon(slotName, createExternalIcon())
// WHEN the internal icon is removed via #removeAllIconsForSlot
underTest.removeAllIconsForSlot(slotName)
// THEN the external icon is removed but the internal icon remains
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
assertThat(iconList.slots[1].hasIconsInSlot()).isFalse() // Indicates removal
verify(iconGroup).onRemoveIcon(1)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_internalUpdatedIndependently() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
val startingExternalIcon =
StatusBarIcon(
"external.package",
UserHandle.ALL,
/* iconId= */ 20,
/* iconLevel= */ 0,
/* number= */ 0,
"externalDescription",
)
underTest.setIcon(slotName, startingExternalIcon)
// WHEN the internal icon is updated
underTest.setIcon(slotName, /* resourceId= */ 11, "newContentDescription")
// THEN only the internal slot gets the updates
val internalSlot = iconList.slots[1]
val internalHolder = internalSlot.getHolderForTag(TAG_PRIMARY)!!
assertThat(internalSlot.name).isEqualTo(slotName)
assertThat(internalHolder.icon!!.contentDescription).isEqualTo("newContentDescription")
assertThat(internalHolder.icon!!.icon.resId).isEqualTo(11)
// And the external slot has its own values
val externalSlot = iconList.slots[0]
val externalHolder = externalSlot.getHolderForTag(TAG_PRIMARY)!!
assertThat(externalSlot.name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(externalHolder.icon!!.contentDescription).isEqualTo("externalDescription")
assertThat(externalHolder.icon!!.icon.resId).isEqualTo(20)
}
/** Regression test for b/255428281. */
@Test
fun internalAndExternalIconWithSameName_externalUpdatedIndependently() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
val startingExternalIcon =
StatusBarIcon(
"external.package",
UserHandle.ALL,
/* iconId= */ 20,
/* iconLevel= */ 0,
/* number= */ 0,
"externalDescription",
)
underTest.setIcon(slotName, startingExternalIcon)
// WHEN the external icon is updated
val newExternalIcon =
StatusBarIcon(
"external.package",
UserHandle.ALL,
/* iconId= */ 21,
/* iconLevel= */ 0,
/* number= */ 0,
"newExternalDescription",
)
underTest.setIcon(slotName, newExternalIcon)
// THEN only the external slot gets the updates
val externalSlot = iconList.slots[0]
val externalHolder = externalSlot.getHolderForTag(TAG_PRIMARY)!!
assertThat(externalSlot.name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(externalHolder.icon!!.contentDescription).isEqualTo("newExternalDescription")
assertThat(externalHolder.icon!!.icon.resId).isEqualTo(21)
// And the internal slot has its own values
val internalSlot = iconList.slots[1]
val internalHolder = internalSlot.getHolderForTag(TAG_PRIMARY)!!
assertThat(internalSlot.name).isEqualTo(slotName)
assertThat(internalHolder.icon!!.contentDescription).isEqualTo("contentDescription")
assertThat(internalHolder.icon!!.icon.resId).isEqualTo(10)
}
@Test
fun externalSlot_alreadyEndsWithSuffix_suffixNotAddedTwice() {
underTest.setIcon("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
assertThat(iconList.slots).hasSize(1)
assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
}
private fun createExternalIcon(): StatusBarIcon {
return StatusBarIcon(
"external.package",
UserHandle.ALL,
/* iconId= */ 2,
/* iconLevel= */ 0,
/* number= */ 0,
"contentDescription",
)
}
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.volume;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -29,7 +28,6 @@ import static org.mockito.Mockito.verify;
import android.app.KeyguardManager;
import android.media.AudioManager;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.InputDevice;
@@ -40,7 +38,6 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -53,9 +50,6 @@ import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -78,8 +72,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
View mDrawerVibrate;
View mDrawerMute;
View mDrawerNormal;
private DeviceConfigProxyFake mDeviceConfigProxy;
private FakeExecutor mExecutor;
@Mock
VolumeDialogController mVolumeDialogController;
@@ -108,9 +100,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
mDeviceConfigProxy = new DeviceConfigProxyFake();
mExecutor = new FakeExecutor(new FakeSystemClock());
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -121,8 +110,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mVolumePanelFactory,
mActivityStarter,
mInteractionJankMonitor,
mDeviceConfigProxy,
mExecutor,
mDumpManager
);
mDialog.init(0, null);
@@ -141,9 +128,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
}
private State createShellState() {
@@ -313,35 +297,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
AudioManager.RINGER_MODE_NORMAL, false);
}
/**
* Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
* API does not exist. So we do the next best thing; we check the cached icon id.
*/
@Test
public void notificationVolumeSeparated_theRingerIconChanges() {
mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
mExecutor.runAllReady(); // for the config change to take effect
// assert icon is new based on res id
assertEquals(mDialog.mVolumeRingerIconDrawableId,
R.drawable.ic_speaker_on);
assertEquals(mDialog.mVolumeRingerMuteIconDrawableId,
R.drawable.ic_speaker_mute);
}
@Test
public void notificationVolumeNotSeparated_theRingerIconRemainsTheSame() {
mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
mExecutor.runAllReady();
assertEquals(mDialog.mVolumeRingerIconDrawableId, R.drawable.ic_volume_ringer);
assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, R.drawable.ic_volume_ringer_mute);
}
/*
@Test
public void testContentDescriptions() {

View File

@@ -97,6 +97,10 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
public void removeAllIconsForSlot(String slot) {
}
@Override
public void removeAllIconsForExternalSlot(String slot) {
}
@Override
public void setIconAccessibilityLiveRegion(String slot, int mode) {
}

View File

@@ -8197,15 +8197,13 @@ public class ActivityManagerService extends IActivityManager.Stub
t.traceEnd();
}
boolean isBootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
// Some systems - like automotive - will explicitly unlock system user then switch
// to a secondary user. Hence, we don't want to send duplicate broadcasts for
// the system user here.
// to a secondary user.
// TODO(b/242195409): this workaround shouldn't be necessary once we move
// the headless-user start logic to UserManager-land.
final boolean isBootingSystemUser = (currentUserId == UserHandle.USER_SYSTEM)
&& !UserManager.isHeadlessSystemUserMode();
if (isBootingSystemUser) {
if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
t.traceBegin("startHomeOnAllDisplays");
mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
t.traceEnd();
@@ -8217,6 +8215,10 @@ public class ActivityManagerService extends IActivityManager.Stub
if (isBootingSystemUser) {
// Need to send the broadcasts for the system user here because
// UserController#startUserInternal will not send them for the system user starting,
// It checks if the user state already exists, which is always the case for the
// system user.
t.traceBegin("sendUserStartBroadcast");
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();

View File

@@ -379,16 +379,11 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
resolvedType = key.requestResolvedType;
}
// Apply any launch flags from the ActivityOptions. This is used only by SystemUI
// to ensure that we can launch the pending intent with a consistent launch mode even
// if the provided PendingIntent is immutable (ie. to force an activity to launch into
// a new task, or to launch multiple instances if supported by the app)
// Apply any launch flags from the ActivityOptions. This is to ensure that the caller
// can specify a consistent launch mode even if the PendingIntent is immutable
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
// TODO(b/254490217): Move this check into SafeActivityOptions
if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
// Extract options before clearing calling identity

View File

@@ -41,7 +41,6 @@ import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -156,7 +155,6 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.System;
import android.service.notification.ZenModeConfig;
@@ -178,7 +176,6 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
@@ -237,7 +234,6 @@ public class AudioService extends IAudioService.Stub
AudioSystemAdapter.OnVolRangeInitRequestListener {
private static final String TAG = "AS.AudioService";
private static final boolean CONFIG_DEFAULT_VAL = false;
private final AudioSystemAdapter mAudioSystem;
private final SystemServerAdapter mSystemServer;
@@ -992,7 +988,6 @@ public class AudioService extends IAudioService.Stub
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper,
AppOpsManager appOps) {
@@ -1032,12 +1027,8 @@ public class AudioService extends IAudioService.Stub
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
mNotifAliasRing = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
ActivityThread.currentApplication().getMainExecutor(),
this::onDeviceConfigChange);
mNotifAliasRing = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
// Initialize volume
// Priority 1 - Android Property
@@ -1255,22 +1246,6 @@ public class AudioService extends IAudioService.Stub
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
}
/**
* Separating notification volume from ring is NOT of aliasing the corresponding streams
* @param properties
*/
private void onDeviceConfigChange(DeviceConfig.Properties properties) {
Set<String> changeSet = properties.getKeyset();
if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
boolean newNotifAliasRing = !properties.getBoolean(
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
if (mNotifAliasRing != newNotifAliasRing) {
mNotifAliasRing = newNotifAliasRing;
updateStreamVolumeAlias(true, TAG);
}
}
}
/**
* Called by handling of MSG_INIT_STREAMS_VOLUMES
*/

View File

@@ -817,9 +817,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mDisplayDeviceConfig = config;
loadFromDisplayDeviceConfig(token, info);
// Since the underlying display-device changed, we really don't know the
// last command that was sent to change it's state. Lets assume it is off and we
// trigger a change immediately.
/// Since the underlying display-device changed, we really don't know the
// last command that was sent to change it's state. Lets assume it is unknown so
// that we trigger a change immediately.
mPowerState.resetScreenState();
}
if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {

View File

@@ -340,20 +340,12 @@ final class DisplayPowerState {
}
/**
* Resets the screen state to {@link Display#STATE_OFF}. Even though we do not know the last
* state that was sent to the underlying display-device, we assume it is off.
*
* We do not set the screen state to {@link Display#STATE_UNKNOWN} to avoid getting in the state
* where PhotonicModulator holds onto the lock. This happens because we currently try to keep
* the mScreenState and mPendingState in sync, however if the screenState is set to
* {@link Display#STATE_UNKNOWN} here, mPendingState will get progressed to this, which will
* force the PhotonicModulator thread to wait onto the lock to take it out of that state.
* b/262294651 for more info.
* Resets the screen state to unknown. Useful when the underlying display-device changes for the
* LogicalDisplay and we do not know the last state that was sent to it.
*/
void resetScreenState() {
mScreenState = Display.STATE_OFF;
mScreenState = Display.STATE_UNKNOWN;
mScreenReady = false;
scheduleScreenUpdate();
}
private void scheduleScreenUpdate() {
@@ -514,6 +506,8 @@ final class DisplayPowerState {
boolean valid = state != Display.STATE_UNKNOWN && !Float.isNaN(brightnessState);
boolean changed = stateChanged || backlightChanged;
if (!valid || !changed) {
mStateChangeInProgress = false;
mBacklightChangeInProgress = false;
try {
mLock.wait();
} catch (InterruptedException ex) {

View File

@@ -95,6 +95,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Slog;
@@ -4973,6 +4974,13 @@ public class UserManagerService extends IUserManager.Stub {
public void setApplicationRestrictions(String packageName, Bundle restrictions,
@UserIdInt int userId) {
checkSystemOrRoot("set application restrictions");
String validationResult = validateName(packageName);
if (validationResult != null) {
if (packageName.contains("../")) {
EventLog.writeEvent(0x534e4554, "239701237", -1, "");
}
throw new IllegalArgumentException("Invalid package name: " + validationResult);
}
if (restrictions != null) {
restrictions.setDefusable(true);
}
@@ -4999,6 +5007,39 @@ public class UserManagerService extends IUserManager.Stub {
mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
}
/**
* Check if the given name is valid.
*
* Note: the logic is taken from FrameworkParsingPackageUtils in master, edited to remove
* unnecessary parts. Copied here for a security fix.
*
* @param name The name to check.
* @return null if it's valid, error message if not
*/
@VisibleForTesting
static String validateName(String name) {
final int n = name.length();
boolean front = true;
for (int i = 0; i < n; i++) {
final char c = name.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
front = false;
continue;
}
if (!front) {
if ((c >= '0' && c <= '9') || c == '_') {
continue;
}
if (c == '.') {
front = true;
continue;
}
}
return "bad character '" + c + "'";
}
return null;
}
private int getUidForPackage(String packageName) {
final long ident = Binder.clearCallingIdentity();
try {