Merge "Apply package name size limit to packages" into main
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 554fe5e..b2838c8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2064,24 +2064,31 @@
}
/**
- * Transfers to a given route for the remote session. The given route must be included in
- * {@link RoutingSessionInfo#getTransferableRoutes()}.
+ * Attempts a transfer to a {@link RoutingSessionInfo#getTransferableRoutes() transferable
+ * route}.
*
+ * <p>Transferring to a transferable route does not require the app to transfer the playback
+ * state from one route to the other. The route provider completely manages the transfer. An
+ * example of provider-managed transfers are the switches between the system's routes, like
+ * the built-in speakers and a BT headset.
+ *
+ * @return True if the transfer is handled by this controller, or false if a new controller
+ * should be created instead.
* @see RoutingSessionInfo#getSelectedRoutes()
* @see RoutingSessionInfo#getTransferableRoutes()
* @see ControllerCallback#onControllerUpdated
*/
- void transferToRoute(@NonNull MediaRoute2Info route) {
+ boolean tryTransferWithinProvider(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
synchronized (mControllerLock) {
if (isReleased()) {
Log.w(TAG, "transferToRoute: Called on released controller. Ignoring.");
- return;
+ return true;
}
if (!mSessionInfo.getTransferableRoutes().contains(route.getId())) {
Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
- return;
+ return false;
}
}
@@ -2096,6 +2103,7 @@
Log.e(TAG, "Unable to transfer to route for session.", ex);
}
}
+ return true;
}
/**
@@ -3587,20 +3595,14 @@
}
RoutingController controller = getCurrentController();
- if (controller
- .getRoutingSessionInfo()
- .getTransferableRoutes()
- .contains(route.getId())) {
- controller.transferToRoute(route);
- return;
+ if (!controller.tryTransferWithinProvider(route)) {
+ requestCreateController(
+ controller,
+ route,
+ MANAGER_REQUEST_ID_NONE,
+ Process.myUserHandle(),
+ mContext.getPackageName());
}
-
- requestCreateController(
- controller,
- route,
- MANAGER_REQUEST_ID_NONE,
- Process.myUserHandle(),
- mContext.getPackageName());
}
@Override
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
index 00225fc..0f6d51d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
@@ -54,6 +54,13 @@
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
val slice by viewModel.buttonSlice.collectAsState()
val label = stringResource(R.string.volume_panel_noise_control_title)
+ val isClickable = viewModel.isClickable(slice)
+ val onClick =
+ if (isClickable) {
+ { ancPopup.show(null) }
+ } else {
+ null
+ }
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(12.dp),
@@ -69,8 +76,9 @@
}
.clip(RoundedCornerShape(28.dp)),
slice = slice,
+ isEnabled = onClick != null,
onWidthChanged = viewModel::onButtonSliceWidthChanged,
- onClick = { ancPopup.show(null) }
+ onClick = onClick,
)
Text(
modifier = Modifier.clearAndSetSemantics {},
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
index 74af3ca..fc5d212 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -32,6 +32,7 @@
fun SliceAndroidView(
slice: Slice?,
modifier: Modifier = Modifier,
+ isEnabled: Boolean = true,
onWidthChanged: ((Int) -> Unit)? = null,
onClick: (() -> Unit)? = null,
) {
@@ -40,7 +41,6 @@
factory = { context: Context ->
ClickableSliceView(
ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
- onClick,
)
.apply {
mode = SliceView.MODE_LARGE
@@ -50,12 +50,14 @@
if (onWidthChanged != null) {
addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
}
- if (onClick != null) {
- setOnClickListener { onClick() }
- }
}
},
- update = { sliceView: SliceView -> sliceView.slice = slice }
+ update = { sliceView: ClickableSliceView ->
+ sliceView.slice = slice
+ sliceView.onClick = onClick
+ sliceView.isEnabled = isEnabled
+ sliceView.isClickable = isEnabled
+ }
)
}
@@ -86,10 +88,9 @@
* first.
*/
@SuppressLint("ViewConstructor") // only used in this class
-private class ClickableSliceView(
- context: Context,
- private val onClick: (() -> Unit)?,
-) : SliceView(context) {
+private class ClickableSliceView(context: Context) : SliceView(context) {
+
+ var onClick: (() -> Unit)? = null
init {
if (onClick != null) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 874c0a2..12debbc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -19,6 +19,7 @@
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -91,7 +92,8 @@
},
onClick = { onCheckedChange(!viewModel.isActive) },
shape = RoundedCornerShape(28.dp),
- colors = colors
+ colors = colors,
+ contentPadding = PaddingValues(0.dp)
) {
Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
index dc96139..dddf582 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -61,6 +61,7 @@
AncSliceRepositoryImpl(
localMediaRepositoryFactory,
testScope.testScheduler,
+ testScope.testScheduler,
sliceViewManager,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
index 384acc4..dd79425 100644
--- a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
@@ -28,6 +28,9 @@
* Returns updating [Slice] for a [sliceUri]. It's null when there is no slice available for the
* provided Uri. This can change overtime because of external changes (like device being
* connected/disconnected).
+ *
+ * The flow should be [kotlinx.coroutines.flow.flowOn] the main thread because [SliceViewManager]
+ * isn't thread-safe. An exception will be thrown otherwise.
*/
fun SliceViewManager.sliceForUri(sliceUri: Uri): Flow<Slice?> =
ConflatedCallbackFlow.conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e980794..d0cebae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -169,6 +169,14 @@
}
}
+ // On the final call to {@link #resetViewState}, the alpha is set back to 1f but
+ // ambientState.isExpansionChanging() is now false. This causes a flicker on the
+ // EmptyShadeView after the shade is collapsed. Make sure the empty shade view
+ // isn't visible unless the shade is expanded.
+ if (view instanceof EmptyShadeView && ambientState.getExpansionFraction() == 0f) {
+ viewState.setAlpha(0f);
+ }
+
// For EmptyShadeView if on keyguard, we need to control the alpha to create
// a nice transition when the user is dragging down the notification panel.
if (view instanceof EmptyShadeView && ambientState.isOnKeyguard()) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
index 8ce3b1f..3117abc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -23,6 +23,7 @@
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.slice.sliceForUri
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import dagger.assisted.Assisted
@@ -57,6 +58,7 @@
constructor(
mediaRepositoryFactory: LocalMediaRepositoryFactory,
@Background private val backgroundCoroutineContext: CoroutineContext,
+ @Main private val mainCoroutineContext: CoroutineContext,
@Assisted private val sliceViewManager: SliceViewManager,
) : AncSliceRepository {
@@ -73,7 +75,7 @@
.distinctUntilChanged()
.flatMapLatest { sliceUri ->
sliceUri ?: return@flatMapLatest flowOf(null)
- sliceViewManager.sliceForUri(sliceUri)
+ sliceViewManager.sliceForUri(sliceUri).flowOn(mainCoroutineContext)
}
.flowOn(backgroundCoroutineContext)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
index bee79bb..c980eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
@@ -16,7 +16,9 @@
package com.android.systemui.volume.panel.component.anc.ui.viewmodel
+import android.content.Intent
import androidx.slice.Slice
+import androidx.slice.SliceItem
import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
@@ -59,6 +61,31 @@
.map { it.buttonSlice }
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ fun isClickable(slice: Slice?): Boolean {
+ slice ?: return false
+ val slices = ArrayDeque<SliceItem>()
+ slices.addAll(slice.items)
+ while (slices.isNotEmpty()) {
+ val item: SliceItem = slices.removeFirst()
+ when (item.format) {
+ android.app.slice.SliceItem.FORMAT_ACTION -> {
+ val itemActionIntent: Intent? = item.action?.intent
+ if (itemActionIntent?.hasExtra(EXTRA_ANC_ENABLED) == true) {
+ return itemActionIntent.getBooleanExtra(EXTRA_ANC_ENABLED, true)
+ }
+ }
+ android.app.slice.SliceItem.FORMAT_SLICE -> {
+ item.slice?.items?.let(slices::addAll)
+ }
+ }
+ }
+ return true
+ }
+
+ private companion object {
+ const val EXTRA_ANC_ENABLED = "EXTRA_ANC_ENABLED"
+ }
+
/** Call this to update [popupSlice] width in a reaction to container size change. */
fun onPopupSliceWidthChanged(width: Int) {
interactor.onPopupSliceWidthChanged(width)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 82725d6..a6fb718 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -394,8 +394,20 @@
}
@Test
+ fun resetViewStates_shadeCollapsed_emptyShadeViewBecomesTransparent() {
+ ambientState.expansionFraction = 0f
+ stackScrollAlgorithm.initView(context)
+ hostView.removeAllViews()
+ hostView.addView(emptyShadeView)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(emptyShadeView.viewState.alpha).isEqualTo(0f)
+ }
+
+ @Test
fun resetViewStates_isOnKeyguard_emptyShadeViewBecomesOpaque() {
- ambientState.setStatusBarState(StatusBarState.SHADE)
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
ambientState.fractionToShade = 0.25f
stackScrollAlgorithm.initView(context)
hostView.removeAllViews()
@@ -403,7 +415,8 @@
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- assertThat(emptyShadeView.viewState.alpha).isEqualTo(1f)
+ val expected = getContentAlpha(ambientState.fractionToShade)
+ assertThat(emptyShadeView.viewState.alpha).isEqualTo(expected)
}
@Test