summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt50
5 files changed, 161 insertions, 15 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 765c5749cd4b..d880aa604849 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -50,6 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel;
import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.connectivity.IconState;
@@ -63,6 +64,7 @@ import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -104,6 +106,8 @@ public class CastTileTest extends SysuiTestCase {
private DialogTransitionAnimator mDialogTransitionAnimator;
@Mock
private QsEventLogger mUiEventLogger;
+ @Mock
+ private CastDetailsViewModel.Factory mCastDetailsViewModelFactory;
private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
private final FakeConnectivityRepository mConnectivityRepository =
@@ -517,6 +521,29 @@ public class CastTileTest extends SysuiTestCase {
assertTrue(mCastTile.getState().forceExpandIcon);
}
+ @Test
+ public void testDetailsViewUnavailableState_returnsNull() {
+ createAndStartTileNewImpl();
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ mCastTile.getDetailsViewModel(Assert::assertNull);
+ }
+
+ @Test
+ public void testDetailsViewAvailableState_returnsNotNull() {
+ createAndStartTileNewImpl();
+ CastDevice device = createConnectedCastDevice();
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+ mConnectivityRepository.setWifiConnected(true);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ mCastTile.getDetailsViewModel(Assert::assertNotNull);
+ }
+
/**
* For simplicity, let this method still set the field even though that's kind of gross
*/
@@ -540,7 +567,8 @@ public class CastTileTest extends SysuiTestCase {
mConnectivityRepository,
mJavaAdapter,
mFeatureFlags,
- mShadeDialogContextInteractor
+ mShadeDialogContextInteractor,
+ mCastDetailsViewModelFactory
);
mCastTile.initialize();
@@ -584,7 +612,8 @@ public class CastTileTest extends SysuiTestCase {
mConnectivityRepository,
mJavaAdapter,
mFeatureFlags,
- mShadeDialogContextInteractor
+ mShadeDialogContextInteractor,
+ mCastDetailsViewModelFactory
);
mCastTile.initialize();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
index d40ecc9565ae..4cd875594b43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
@@ -43,6 +43,8 @@ import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsViewModel
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
+import com.android.systemui.qs.tiles.dialog.CastDetailsContent
+import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel
import com.android.systemui.qs.tiles.dialog.InternetDetailsContent
import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
import com.android.systemui.qs.tiles.dialog.ModesDetailsContent
@@ -131,6 +133,7 @@ private fun MapTileDetailsContent(tileDetailsViewModel: TileDetailsViewModel) {
is BluetoothDetailsViewModel ->
BluetoothDetailsContent(tileDetailsViewModel.detailsContentViewModel)
is ModesDetailsViewModel -> ModesDetailsContent(tileDetailsViewModel)
+ is CastDetailsViewModel -> CastDetailsContent()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index c60e3da9d833..349c771048dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -48,11 +48,13 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.qs.TileDetailsViewModel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel;
import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -93,6 +95,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
private boolean mCastTransportAllowed;
private boolean mHotspotConnected;
+ private final CastDetailsViewModel.Factory mCastDetailsViewModelFactory;
@Inject
public CastTile(
@@ -113,7 +116,8 @@ public class CastTile extends QSTileImpl<BooleanState> {
ConnectivityRepository connectivityRepository,
TileJavaAdapter javaAdapter,
FeatureFlags featureFlags,
- ShadeDialogContextInteractor shadeDialogContextInteractor
+ ShadeDialogContextInteractor shadeDialogContextInteractor,
+ CastDetailsViewModel.Factory castDetailsViewModelFactory
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -124,6 +128,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
mJavaAdapter = javaAdapter;
mFeatureFlags = featureFlags;
mShadeDialogContextInteractor = shadeDialogContextInteractor;
+ mCastDetailsViewModelFactory = castDetailsViewModelFactory;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
@@ -172,12 +177,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
@Override
protected void handleClick(@Nullable Expandable expandable) {
- if (getState().state == Tile.STATE_UNAVAILABLE) {
- return;
- }
-
- List<CastDevice> activeDevices = getActiveDevices();
- if (willPopDialog()) {
+ handleClick(() -> {
if (!mKeyguard.isShowing()) {
showDialog(expandable);
} else {
@@ -187,16 +187,43 @@ public class CastTile extends QSTileImpl<BooleanState> {
showDialog(null /* view */);
});
}
+ });
+ }
+
+ @Override
+ public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
+ CastDetailsViewModel viewModel = mCastDetailsViewModelFactory.create();
+ handleClick(() -> {
+ if (!mKeyguard.isShowing()) {
+ callback.accept(viewModel);
+ } else {
+ mActivityStarter.dismissKeyguardThenExecute(() -> {
+ callback.accept(viewModel);
+ return false;
+ }, null /* cancelAction */, true/* afterKeyguardGone */);
+ }
+ });
+ return true;
+ }
+
+ private void handleClick(Runnable showPromptCallback) {
+ if (getState().state == Tile.STATE_UNAVAILABLE) {
+ return;
+ }
+
+ List<CastDevice> activeDevices = getActiveDevices();
+ if (willShowPrompt()) {
+ showPromptCallback.run();
} else {
mController.stopCasting(activeDevices.get(0), StopReason.STOP_QS_TILE);
}
}
- // We want to pop up the media route selection dialog if we either have no active devices
- // (neither routes nor projection), or if we have an active route. In other cases, we assume
- // that a projection is active. This is messy, but this tile never correctly handled the
- // case where multiple devices were active :-/.
- private boolean willPopDialog() {
+ // We want to pop up the media route selection dialog (or show the cast details view) if we
+ // either have no active devices (neither routes nor projection), or if we have an active
+ // route. In other cases, we assume that a projection is active. This is messy, but this tile
+ // never correctly handled the case where multiple devices were active :-/.
+ private boolean willShowPrompt() {
List<CastDevice> activeDevices = getActiveDevices();
return activeDevices.isEmpty() || (activeDevices.get(0).getTag() instanceof RouteInfo);
}
@@ -303,7 +330,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
state.secondaryLabel = "";
}
state.expandedAccessibilityClassName = Button.class.getName();
- state.forceExpandIcon = willPopDialog();
+ state.forceExpandIcon = willShowPrompt();
} else {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_network);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
new file mode 100644
index 000000000000..e31108ea450b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.internal.R
+
+@Composable
+fun CastDetailsContent() {
+ // TODO(b/378514236): Finish implementing this function.
+ AndroidView(
+ modifier = Modifier.fillMaxWidth().fillMaxHeight(),
+ factory = { context ->
+ // Inflate with the existing dialog xml layout
+ LayoutInflater.from(context).inflate(R.layout.media_route_controller_dialog, null)
+ },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
new file mode 100644
index 000000000000..efe04a25244f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** The view model used for the screen record details view in the Quick Settings */
+class CastDetailsViewModel
+@AssistedInject
+constructor(private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler) :
+ TileDetailsViewModel {
+ @AssistedFactory
+ fun interface Factory {
+ fun create(): CastDetailsViewModel
+ }
+
+ override fun clickOnSettingsButton() {
+ qsTileIntentUserActionHandler.handle(
+ /* expandable= */ null,
+ Intent(Settings.ACTION_CAST_SETTINGS),
+ )
+ }
+
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file,
+ override val title: String
+ get() = "Cast screen to device"
+
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file,
+ override val subTitle: String
+ get() = "Searching for devices..."
+}