[Partial Screensharing] Add a temporary entrypoint in SysUI screen recorder
Adds a button that allows to start app selection flow in SystemUI screen recorder dialog (see screenshot in the bug). This button is needed for teamfood testing of partial screensharing and will be shown only when the feature flag is enabled. It will be removed when redesigned dialog with a dropdown will be implemented. Bug: 239422418 Test: turn on feature flag and start partial screen recording Test: atest com.android.systemui.screenrecord.ScreenRecordDialogTest Change-Id: I9caa913ef52eeb2203dca32e94e0b10d71fc4b83
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
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
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user