diff options
11 files changed, 341 insertions, 25 deletions
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml index f57d65aef8af..ab38dd2aebba 100644 --- a/packages/SystemUI/res/layout/screen_record_dialog.xml +++ b/packages/SystemUI/res/layout/screen_record_dialog.xml @@ -148,6 +148,20 @@ android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/> + + <!-- Temporary entrypoint for the partial screensharing used for teamfooding --> + <!-- TODO(b/236838395) remove this and use redesigned dialog --> + <TextView + android:id="@+id/button_app" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="0" + android:layout_gravity="end" + android:layout_marginEnd="8dp" + android:text="App" + style="@style/Widget.Dialog.Button.BorderButton" /> + <TextView android:id="@+id/button_start" android:layout_width="wrap_content" diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt index 6802da347c93..53abd9948d03 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -22,6 +22,7 @@ import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION import android.os.Binder import android.os.Bundle import android.os.IBinder +import android.os.ResultReceiver import android.view.View import com.android.internal.app.ChooserActivity import com.android.internal.app.chooser.NotSelectableTargetInfo @@ -103,17 +104,42 @@ class MediaProjectionAppSelectorActivity @Inject constructor( } private fun onTargetActivityLaunched(launchToken: IBinder) { - val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION) - val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder) - - projection.launchCookie = launchToken + if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) { + // The client requested to return the result in the result receiver instead of + // activity result, let's send the media projection to the result receiver + val resultReceiver = intent + .getParcelableExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER, + ResultReceiver::class.java) as ResultReceiver + val captureRegion = MediaProjectionCaptureTarget(launchToken) + val data = Bundle().apply { + putParcelable(KEY_CAPTURE_TARGET, captureRegion) + } + resultReceiver.send(RESULT_OK, data) + } else { + // Return the media projection instance as activity result + val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION) + val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder) + + projection.launchCookie = launchToken + + val intent = Intent() + intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder()) + setResult(RESULT_OK, intent) + setForceSendResultForMediaProjection() + } - val intent = Intent() - intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder()) - setResult(RESULT_OK, intent) - setForceSendResultForMediaProjection() finish() } override fun shouldGetOnlyDefaultActivities() = false + + companion object { + /** + * When EXTRA_CAPTURE_REGION_RESULT_RECEIVER is passed as intent extra + * the activity will send the [CaptureRegion] to the result receiver + * instead of returning media projection instance through activity result. + */ + const val EXTRA_CAPTURE_REGION_RESULT_RECEIVER = "capture_region_result_receiver" + const val KEY_CAPTURE_TARGET = "capture_region" + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt new file mode 100644 index 000000000000..fbf9294a0a37 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt @@ -0,0 +1,49 @@ +/* + * 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. + */ + +package com.android.systemui.media + +import android.os.IBinder +import android.os.Parcel +import android.os.Parcelable + +/** + * Class that represents an area that should be captured. + * Currently it has only a launch cookie that represents a task but + * we potentially could add more identifiers e.g. for a pair of tasks. + */ +data class MediaProjectionCaptureTarget( + val launchCookie: IBinder? +): Parcelable { + + constructor(parcel: Parcel) : this(parcel.readStrongBinder()) + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeStrongBinder(launchCookie) + } + + override fun describeContents(): Int = 0 + + companion object CREATOR : Parcelable.Creator<MediaProjectionCaptureTarget> { + override fun createFromParcel(parcel: Parcel): MediaProjectionCaptureTarget { + return MediaProjectionCaptureTarget(parcel) + } + + override fun newArray(size: Int): Array<MediaProjectionCaptureTarget?> { + return arrayOfNulls(size) + } + } +} 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 02d30c5224a0..75fb393d84b5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -34,6 +34,7 @@ import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +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; @@ -61,6 +62,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> private final KeyguardStateController mKeyguardStateController; private final Callback mCallback = new Callback(); private final DialogLaunchAnimator mDialogLaunchAnimator; + private final FeatureFlags mFlags; private long mMillisUntilFinished = 0; @@ -71,6 +73,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> @Main Handler mainHandler, FalsingManager falsingManager, MetricsLogger metricsLogger, + FeatureFlags flags, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, @@ -83,6 +86,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> statusBarStateController, activityStarter, qsLogger); mController = controller; mController.observe(this, mCallback); + mFlags = flags; mKeyguardDismissUtil = keyguardDismissUtil; mKeyguardStateController = keyguardStateController; mDialogLaunchAnimator = dialogLaunchAnimator; @@ -164,8 +168,8 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); getHost().collapsePanels(); }; - ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext, - onStartRecordingClicked); + ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext, mFlags, + mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked); ActivityStarter.OnDismissAction dismissAction = () -> { if (shouldAnimateFromView) { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index 1a08878cecfe..1083f22164d9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java @@ -29,8 +29,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.policy.CallbackController; @@ -94,9 +97,11 @@ public class RecordingController } /** Create a dialog to show screen recording options to the user. */ - public ScreenRecordDialog createScreenRecordDialog(Context context, + public ScreenRecordDialog createScreenRecordDialog(Context context, FeatureFlags flags, + DialogLaunchAnimator dialogLaunchAnimator, ActivityStarter activityStarter, @Nullable Runnable onStartRecordingClicked) { - return new ScreenRecordDialog(context, this, mUserContextProvider, onStartRecordingClicked); + return new ScreenRecordDialog(context, this, activityStarter, mUserContextProvider, + flags, dialogLaunchAnimator, onStartRecordingClicked); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index a837cbb8a50e..047762662aab 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -16,6 +16,7 @@ package com.android.systemui.screenrecord; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -42,6 +43,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.media.MediaProjectionCaptureTarget; import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -67,6 +69,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private static final String EXTRA_PATH = "extra_path"; private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio"; private static final String EXTRA_SHOW_TAPS = "extra_showTaps"; + private static final String EXTRA_CAPTURE_TARGET = "extra_captureTarget"; private static final String ACTION_START = "com.android.systemui.screenrecord.START"; private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP"; @@ -110,14 +113,18 @@ public class RecordingService extends Service implements ScreenMediaRecorderList * @param audioSource The ordinal value of the audio source * {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource} * @param showTaps True to make touches visible while recording + * @param captureTarget pass this parameter to capture a specific part instead + * of the full screen */ public static Intent getStartIntent(Context context, int resultCode, - int audioSource, boolean showTaps) { + int audioSource, boolean showTaps, + @Nullable MediaProjectionCaptureTarget captureTarget) { return new Intent(context, RecordingService.class) .setAction(ACTION_START) .putExtra(EXTRA_RESULT_CODE, resultCode) .putExtra(EXTRA_AUDIO_SOURCE, audioSource) - .putExtra(EXTRA_SHOW_TAPS, showTaps); + .putExtra(EXTRA_SHOW_TAPS, showTaps) + .putExtra(EXTRA_CAPTURE_TARGET, captureTarget); } @Override @@ -136,6 +143,9 @@ public class RecordingService extends Service implements ScreenMediaRecorderList .values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)]; Log.d(TAG, "recording with audio source" + mAudioSource); mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false); + MediaProjectionCaptureTarget captureTarget = + intent.getParcelableExtra(EXTRA_CAPTURE_TARGET, + MediaProjectionCaptureTarget.class); mOriginalShowTaps = Settings.System.getInt( getApplicationContext().getContentResolver(), @@ -148,6 +158,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList mMainHandler, currentUserId, mAudioSource, + captureTarget, this ); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index d098b4b3442a..b8d96f774e02 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -51,6 +51,7 @@ import android.util.Size; import android.view.Surface; import android.view.WindowManager; +import com.android.systemui.media.MediaProjectionCaptureTarget; import java.io.File; import java.io.Closeable; import java.io.IOException; @@ -85,6 +86,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private ScreenRecordingMuxer mMuxer; private ScreenInternalAudioRecorder mAudio; private ScreenRecordingAudioSource mAudioSource; + private final MediaProjectionCaptureTarget mCaptureRegion; private final Handler mHandler; private Context mContext; @@ -92,10 +94,12 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { public ScreenMediaRecorder(Context context, Handler handler, int user, ScreenRecordingAudioSource audioSource, + MediaProjectionCaptureTarget captureRegion, ScreenMediaRecorderListener listener) { mContext = context; mHandler = handler; mUser = user; + mCaptureRegion = captureRegion; mListener = listener; mAudioSource = audioSource; } @@ -108,9 +112,11 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { IMediaProjection proj = null; proj = mediaService.createProjection(mUser, mContext.getPackageName(), MediaProjectionManager.TYPE_SCREEN_CAPTURE, false); - IBinder projection = proj.asBinder(); - mMediaProjection = new MediaProjection(mContext, - IMediaProjection.Stub.asInterface(projection)); + IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder()); + if (mCaptureRegion != null) { + projection.setLaunchCookie(mCaptureRegion.getLaunchCookie()); + } + mMediaProjection = new MediaProjection(mContext, projection); mMediaProjection.registerCallback(this, mHandler); File cacheDir = mContext.getCacheDir(); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java index 1fb88dfe9b52..efa45a4c4723 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java @@ -16,6 +16,10 @@ package com.android.systemui.screenrecord; +import static android.app.Activity.RESULT_OK; + +import static com.android.systemui.media.MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER; +import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET; import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL; import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC; import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL; @@ -24,8 +28,13 @@ import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; +import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.ResultReceiver; import android.view.Gravity; +import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.ArrayAdapter; @@ -36,6 +45,13 @@ import android.widget.TextView; import androidx.annotation.Nullable; import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.DialogLaunchAnimator; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.media.MediaProjectionAppSelectorActivity; +import com.android.systemui.media.MediaProjectionCaptureTarget; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.SystemUIDialog; @@ -55,15 +71,23 @@ public class ScreenRecordDialog extends SystemUIDialog { private final UserContextProvider mUserContextProvider; @Nullable private final Runnable mOnStartRecordingClicked; + private final ActivityStarter mActivityStarter; + private final FeatureFlags mFlags; + private final DialogLaunchAnimator mDialogLaunchAnimator; private Switch mTapsSwitch; private Switch mAudioSwitch; private Spinner mOptions; public ScreenRecordDialog(Context context, RecordingController controller, - UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) { + ActivityStarter activityStarter, UserContextProvider userContextProvider, + FeatureFlags flags, DialogLaunchAnimator dialogLaunchAnimator, + @Nullable Runnable onStartRecordingClicked) { super(context); mController = controller; mUserContextProvider = userContextProvider; + mActivityStarter = activityStarter; + mDialogLaunchAnimator = dialogLaunchAnimator; + mFlags = flags; mOnStartRecordingClicked = onStartRecordingClicked; } @@ -91,10 +115,36 @@ public class ScreenRecordDialog extends SystemUIDialog { mOnStartRecordingClicked.run(); } - requestScreenCapture(); + // Start full-screen recording + requestScreenCapture(/* captureTarget= */ null); dismiss(); }); + if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) { + TextView appBtn = findViewById(R.id.button_app); + + appBtn.setVisibility(View.VISIBLE); + appBtn.setOnClickListener(v -> { + Intent intent = new Intent(getContext(), MediaProjectionAppSelectorActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // We can't start activity for result here so we use result receiver to get + // the selected target to capture + intent.putExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER, + new CaptureTargetResultReceiver()); + + ActivityLaunchAnimator.Controller animationController = + mDialogLaunchAnimator.createActivityLaunchController(appBtn); + + if (animationController == null) { + dismiss(); + } + + mActivityStarter.startActivity(intent, /* dismissShade= */ true, + animationController); + }); + } + mAudioSwitch = findViewById(R.id.screenrecord_audio_switch); mTapsSwitch = findViewById(R.id.screenrecord_taps_switch); mOptions = findViewById(R.id.screen_recording_options); @@ -108,7 +158,12 @@ public class ScreenRecordDialog extends SystemUIDialog { }); } - private void requestScreenCapture() { + /** + * Starts screen capture after some countdown + * @param captureTarget target to capture (could be e.g. a task) or + * null to record the whole screen + */ + private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) { Context userContext = mUserContextProvider.getUserContext(); boolean showTaps = mTapsSwitch.isChecked(); ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked() @@ -118,7 +173,7 @@ public class ScreenRecordDialog extends SystemUIDialog { RecordingService.REQUEST_CODE, RecordingService.getStartIntent( userContext, Activity.RESULT_OK, - audioMode.ordinal(), showTaps), + audioMode.ordinal(), showTaps, captureTarget), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); PendingIntent stopIntent = PendingIntent.getService(userContext, RecordingService.REQUEST_CODE, @@ -126,4 +181,22 @@ public class ScreenRecordDialog extends SystemUIDialog { PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent); } + + private class CaptureTargetResultReceiver extends ResultReceiver { + + CaptureTargetResultReceiver() { + super(new Handler(Looper.getMainLooper())); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == RESULT_OK) { + MediaProjectionCaptureTarget captureTarget = resultData + .getParcelable(KEY_CAPTURE_TARGET, MediaProjectionCaptureTarget.class); + + // Start recording of the selected target + requestScreenCapture(captureTarget); + } + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index e4c5299a0cc5..e6bd396ebc6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -38,6 +39,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSTileHost; @@ -73,6 +75,8 @@ public class ScreenRecordTileTest extends SysuiTestCase { @Mock private QSLogger mQSLogger; @Mock + private FeatureFlags mFeatureFlags; + @Mock private KeyguardStateController mKeyguardStateController; @Mock private DialogLaunchAnimator mDialogLaunchAnimator; @@ -94,6 +98,7 @@ public class ScreenRecordTileTest extends SysuiTestCase { new Handler(mTestableLooper.getLooper()), new FalsingManagerFake(), mMetricsLogger, + mFeatureFlags, mStatusBarStateController, mActivityStarter, mQSLogger, @@ -125,7 +130,8 @@ public class ScreenRecordTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); ArgumentCaptor<Runnable> onStartRecordingClicked = ArgumentCaptor.forClass(Runnable.class); - verify(mController).createScreenRecordDialog(any(), onStartRecordingClicked.capture()); + verify(mController).createScreenRecordDialog(any(), eq(mFeatureFlags), + eq(mDialogLaunchAnimator), eq(mActivityStarter), onStartRecordingClicked.capture()); // When starting the recording, we collapse the shade and disable the dialog animation. assertNotNull(onStartRecordingClicked.getValue()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index b05d9a31b475..a1d78cbba7f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.verify; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; +import android.os.Binder; import android.os.Handler; import android.os.RemoteException; import android.testing.AndroidTestingRunner; @@ -38,6 +39,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; +import com.android.systemui.media.MediaProjectionCaptureTarget; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -108,8 +110,17 @@ public class RecordingServiceTest extends SysuiTestCase { } @Test - public void testLogStartRecording() { - Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false); + public void testLogStartFullScreenRecording() { + Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, null); + mRecordingService.onStartCommand(startIntent, 0, 0); + + verify(mUiEventLogger, times(1)).log(Events.ScreenRecordEvent.SCREEN_RECORD_START); + } + + @Test + public void testLogStartPartialRecording() { + MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new Binder()); + Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, target); mRecordingService.onStartCommand(startIntent, 0, 0); verify(mUiEventLogger, times(1)).log(Events.ScreenRecordEvent.SCREEN_RECORD_START); @@ -142,7 +153,7 @@ public class RecordingServiceTest extends SysuiTestCase { // When the screen recording does not start properly doThrow(new RuntimeException("fail")).when(mScreenMediaRecorder).start(); - Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false); + Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, null); mRecordingService.onStartCommand(startIntent, 0, 0); // Then the state is set to not recording diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt new file mode 100644 index 000000000000..03d944475da8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt @@ -0,0 +1,111 @@ +/* + * 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. + */ + +/* + * 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. + */ + +package com.android.systemui.screenrecord + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserContextProvider +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class ScreenRecordDialogTest : SysuiTestCase() { + + @Mock + private lateinit var starter: ActivityStarter + @Mock + private lateinit var controller: RecordingController + @Mock + private lateinit var userContextProvider: UserContextProvider + @Mock + private lateinit var flags: FeatureFlags + @Mock + private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Mock + private lateinit var onStartRecordingClicked: Runnable + + private lateinit var dialog: ScreenRecordDialog + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + dialog = ScreenRecordDialog( + context, controller, starter, userContextProvider, flags, dialogLaunchAnimator, + onStartRecordingClicked + ) + } + + @After + fun teardown() { + if (::dialog.isInitialized) { + dialog.dismiss() + } + } + + @Test + fun testShowDialog_partialScreenSharingDisabled_appButtonIsNotVisible() { + whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false) + + dialog.show() + + val visibility = dialog.requireViewById<View>(R.id.button_app).visibility + assertThat(visibility).isEqualTo(View.GONE) + } + + @Test + fun testShowDialog_partialScreenSharingEnabled_appButtonIsVisible() { + whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true) + + dialog.show() + + val visibility = dialog.requireViewById<View>(R.id.button_app).visibility + assertThat(visibility).isEqualTo(View.VISIBLE) + } +} |