diff options
| author | 2025-01-08 11:01:21 -0800 | |
|---|---|---|
| committer | 2025-01-08 11:01:21 -0800 | |
| commit | bdbeef9e76c0fc86d71033941e9557d5e4191a36 (patch) | |
| tree | 1a5b4b8c8197b74488df67ea8028595b4de704ce | |
| parent | 84aa0ce49a4bc74ebb195baa3f337394a4404d93 (diff) | |
| parent | 33788ae6c025217d09aef03ebbf054f4136ca59a (diff) | |
Merge "QSDetailedView: Add getDetailsViewModel to ScreenRecordTile" into main
3 files changed, 158 insertions, 47 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index fc1d73b62abd..3a3f5371d195 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.app.Dialog; import android.media.projection.StopReason; import android.os.Handler; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.service.quicksettings.Tile; import android.testing.TestableLooper; @@ -52,6 +53,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; +import com.android.systemui.qs.flags.QsDetailedView; import com.android.systemui.qs.flags.QsInCompose; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; @@ -63,6 +65,7 @@ import com.android.systemui.statusbar.phone.KeyguardDismissUtil; 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; @@ -70,11 +73,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; +import java.util.List; + @RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest @@ -82,7 +85,8 @@ public class ScreenRecordTileTest extends SysuiTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { - return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX); + return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX, + QsDetailedView.FLAG_NAME); } @Mock @@ -336,6 +340,30 @@ public class ScreenRecordTileTest extends SysuiTestCase { .notifyPermissionRequestDisplayed(mContext.getUserId()); } + @Test + @EnableFlags(QsDetailedView.FLAG_NAME) + public void testNotStartingAndRecording_returnDetailsViewModel() { + when(mController.isStarting()).thenReturn(false); + when(mController.isRecording()).thenReturn(false); + mTile.getDetailsViewModel(Assert::assertNotNull); + } + + @Test + @EnableFlags(QsDetailedView.FLAG_NAME) + public void testStarting_notReturnDetailsViewModel() { + when(mController.isStarting()).thenReturn(true); + when(mController.isRecording()).thenReturn(false); + mTile.getDetailsViewModel(Assert::assertNull); + } + + @Test + @EnableFlags(QsDetailedView.FLAG_NAME) + public void testRecording_notReturnDetailsViewModel() { + when(mController.isStarting()).thenReturn(false); + when(mController.isRecording()).thenReturn(true); + mTile.getDetailsViewModel(Assert::assertNull); + } + private QSTile.Icon createExpectedIcon(int resId) { if (QsInCompose.isEnabled()) { return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index ec8d30b01eab..e93cec875429 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -41,12 +41,14 @@ import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile; +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.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.qs.tiles.dialog.ScreenRecordDetailsViewModel; import com.android.systemui.res.R; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.screenrecord.data.model.ScreenRecordModel; @@ -54,6 +56,8 @@ import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; +import java.util.function.Consumer; + import javax.inject.Inject; /** @@ -122,17 +126,78 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> @Override protected void handleClick(@Nullable Expandable expandable) { + handleClick(() -> showDialog(expandable)); + } + + private void showDialog(@Nullable Expandable expandable) { + final Dialog dialog = mController.createScreenRecordDialog( + this::onStartRecordingClicked); + + executeWhenUnlockedKeyguard(() -> { + // We animate from the touched view only if we are not on the keyguard, given that if we + // are we will dismiss it which will also collapse the shade. + boolean shouldAnimateFromExpandable = + expandable != null && !mKeyguardStateController.isShowing(); + + if (shouldAnimateFromExpandable) { + DialogTransitionAnimator.Controller controller = + expandable.dialogTransitionController(new DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG)); + if (controller != null) { + mDialogTransitionAnimator.show(dialog, + controller, /* animateBackgroundBoundsChange= */ true); + } else { + dialog.show(); + } + } else { + dialog.show(); + } + }); + } + + private void onStartRecordingClicked() { + // We dismiss the shade. Since starting the recording will also dismiss the dialog (if + // there is one showing), we disable the exit animation which looks weird when it happens + // at the same time as the shade collapsing. + mDialogTransitionAnimator.disableAllCurrentDialogsExitAnimations(); + mPanelInteractor.collapsePanels(); + } + + private void executeWhenUnlockedKeyguard(Runnable dismissActionCallback) { + ActivityStarter.OnDismissAction dismissAction = () -> { + dismissActionCallback.run(); + + int uid = mUserContextProvider.getUserContext().getUserId(); + mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed(uid); + + return false; + }; + + mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false /* requiresShadeOpen */, + true /* afterKeyguardDone */); + } + + private void handleClick(Runnable showPromptCallback) { if (mController.isStarting()) { cancelCountdown(); } else if (mController.isRecording()) { stopRecording(); } else { - mUiHandler.post(() -> showPrompt(expandable)); + mUiHandler.post(showPromptCallback); } refreshState(); } @Override + public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) { + handleClick(() -> + callback.accept(new ScreenRecordDetailsViewModel()) + ); + return true; + } + + @Override protected void handleUpdateState(BooleanState state, Object arg) { boolean isStarting = mController.isStarting(); boolean isRecording = mController.isRecording(); @@ -178,49 +243,6 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> return mContext.getString(R.string.quick_settings_screen_record_label); } - private void showPrompt(@Nullable Expandable expandable) { - // We animate from the touched view only if we are not on the keyguard, given that if we - // are we will dismiss it which will also collapse the shade. - boolean shouldAnimateFromExpandable = - expandable != null && !mKeyguardStateController.isShowing(); - - // Create the recording dialog that will collapse the shade only if we start the recording. - Runnable onStartRecordingClicked = () -> { - // We dismiss the shade. Since starting the recording will also dismiss the dialog, we - // disable the exit animation which looks weird when it happens at the same time as the - // shade collapsing. - mDialogTransitionAnimator.disableAllCurrentDialogsExitAnimations(); - mPanelInteractor.collapsePanels(); - }; - - final Dialog dialog = mController.createScreenRecordDialog(onStartRecordingClicked); - - ActivityStarter.OnDismissAction dismissAction = () -> { - if (shouldAnimateFromExpandable) { - DialogTransitionAnimator.Controller controller = - expandable.dialogTransitionController(new DialogCuj( - InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, - INTERACTION_JANK_TAG)); - if (controller != null) { - mDialogTransitionAnimator.show(dialog, - controller, /* animateBackgroundBoundsChange= */ true); - } else { - dialog.show(); - } - } else { - dialog.show(); - } - - int uid = mUserContextProvider.getUserContext().getUserId(); - mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed(uid); - - return false; - }; - - mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false /* requiresShadeOpen */, - true /* afterKeyguardDone */); - } - private void cancelCountdown() { Log.d(TAG, "Cancelling countdown"); mController.cancelCountdown(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt new file mode 100644 index 000000000000..42cb1248ccff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt @@ -0,0 +1,61 @@ +/* + * 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.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import com.android.systemui.plugins.qs.TileDetailsViewModel +import com.android.systemui.res.R + +/** The view model used for the screen record details view in the Quick Settings */ +class ScreenRecordDetailsViewModel() : TileDetailsViewModel() { + @Composable + override fun GetContentView() { + // TODO(b/378514312): Finish implementing this function. + AndroidView( + modifier = Modifier.fillMaxWidth().heightIn(max = VIEW_MAX_HEIGHT), + factory = { context -> + // Inflate with the existing dialog xml layout + LayoutInflater.from(context).inflate(R.layout.screen_share_dialog, null) + }, + ) + } + + override fun clickOnSettingsButton() { + // No settings button in this tile. + } + + override fun getTitle(): String { + // TODO(b/388321032): Replace this string with a string in a translatable xml file, + return "Screen recording" + } + + override fun getSubTitle(): String { + // No sub-title in this tile. + return "" + } + + companion object { + private val VIEW_MAX_HEIGHT: Dp = 320.dp + } +} |