Merge "[Partial Screensharing] Add a temporary entrypoint in SysUI screen recorder" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
5837f00409
@@ -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