Merge "[Partial Screensharing] Permission Dialog initial version" into tm-qpr-dev

This commit is contained in:
Yalan Yiue
2022-11-09 11:30:26 +00:00
committed by Android (Google) Code Review
9 changed files with 597 additions and 26 deletions

View File

@@ -0,0 +1,86 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="@dimen/screenrecord_option_icon_size"
android:layout_height="@dimen/screenrecord_option_icon_size"
android:src="@drawable/ic_mic_26dp"
android:tint="?android:attr/textColorSecondary"
android:layout_gravity="center_vertical"
android:layout_weight="0"
android:layout_marginRight="@dimen/screenrecord_option_padding"
android:importantForAccessibility="no"/>
<Spinner
android:id="@+id/screen_recording_options"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_weight="1"
android:popupBackground="@drawable/screenrecord_spinner_background"
android:dropDownWidth="274dp"
android:importantForAccessibility="yes"/>
<Switch
android:layout_width="wrap_content"
android:minWidth="48dp"
android:layout_height="48dp"
android:layout_weight="0"
android:layout_gravity="end"
android:id="@+id/screenrecord_audio_switch"
style="@style/ScreenRecord.Switch"
android:importantForAccessibility="yes"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/screenrecord_option_padding">
<ImageView
android:layout_width="@dimen/screenrecord_option_icon_size"
android:layout_height="@dimen/screenrecord_option_icon_size"
android:layout_weight="0"
android:src="@drawable/ic_touch"
android:tint="?android:attr/textColorSecondary"
android:layout_gravity="center_vertical"
android:layout_marginRight="@dimen/screenrecord_option_padding"
android:importantForAccessibility="no"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:minHeight="48dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/screenrecord_taps_label"
android:textAppearance="?android:attr/textAppearanceMedium"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textColor="?android:attr/textColorPrimary"
android:contentDescription="@string/screenrecord_taps_label"/>
<Switch
android:layout_width="wrap_content"
android:minWidth="48dp"
android:layout_height="48dp"
android:layout_weight="0"
android:id="@+id/screenrecord_taps_switch"
style="@style/ScreenRecord.Switch"
android:importantForAccessibility="yes"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,94 @@
<!--
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.
-->
<!-- Scrollview is necessary to fit everything in landscape layout -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/screen_share_permission_dialog"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dialog_side_padding"
android:paddingEnd="@dimen/dialog_side_padding"
android:paddingTop="@dimen/dialog_top_padding"
android:paddingBottom="@dimen/dialog_bottom_padding"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:layout_width="@dimen/screenrecord_logo_size"
android:layout_height="@dimen/screenrecord_logo_size"
android:src="@drawable/ic_screenrecord"
android:tint="@color/screenrecord_icon_color"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/screen_share_dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:layout_marginTop="22dp"
android:layout_marginBottom="15dp"/>
<Spinner
android:id="@+id/screen_share_mode_spinner"
android:layout_width="320dp"
android:layout_height="72dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp" />
<ViewStub
android:id="@+id/options_stub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text_warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screenrecord_description"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:gravity="start"
android:layout_marginBottom="20dp"/>
<!-- Buttons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="36dp">
<TextView
android:id="@+id/button_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/cancel"
style="@style/Widget.Dialog.Button.BorderButton" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<TextView
android:id="@+id/button_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/screenrecord_start"
style="@style/Widget.Dialog.Button" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -18,6 +18,9 @@ package com.android.systemui.media;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
@@ -44,6 +47,8 @@ import com.android.systemui.Dependency;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags; import com.android.systemui.flags.Flags;
import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils; import com.android.systemui.util.Utils;
@@ -102,7 +107,9 @@ public class MediaProjectionPermissionActivity extends Activity
CharSequence dialogText = null; CharSequence dialogText = null;
CharSequence dialogTitle = null; CharSequence dialogTitle = null;
String appName = null;
if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) { if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) {
// TODO(b/253438807): handle special app name
dialogText = getString(R.string.media_projection_dialog_service_text); dialogText = getString(R.string.media_projection_dialog_service_text);
dialogTitle = getString(R.string.media_projection_dialog_service_title); dialogTitle = getString(R.string.media_projection_dialog_service_title);
} else { } else {
@@ -132,7 +139,7 @@ public class MediaProjectionPermissionActivity extends Activity
String unsanitizedAppName = TextUtils.ellipsize(label, String unsanitizedAppName = TextUtils.ellipsize(label,
paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString(); paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName); appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
String actionText = getString(R.string.media_projection_dialog_text, appName); String actionText = getString(R.string.media_projection_dialog_text, appName);
SpannableString message = new SpannableString(actionText); SpannableString message = new SpannableString(actionText);
@@ -146,27 +153,28 @@ public class MediaProjectionPermissionActivity extends Activity
dialogTitle = getString(R.string.media_projection_dialog_title, appName); dialogTitle = getString(R.string.media_projection_dialog_title, appName);
} }
if (isPartialScreenSharingEnabled()) {
mDialog = new MediaProjectionPermissionDialog(this, () -> {
ScreenShareOption selectedOption =
((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption();
grantMediaProjectionPermission(selectedOption.getMode());
}, appName);
} else {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
R.style.Theme_SystemUI_Dialog) R.style.Theme_SystemUI_Dialog)
.setTitle(dialogTitle) .setTitle(dialogTitle)
.setIcon(R.drawable.ic_media_projection_permission) .setIcon(R.drawable.ic_media_projection_permission)
.setMessage(dialogText) .setMessage(dialogText)
.setPositiveButton(R.string.media_projection_action_text, this) .setPositiveButton(R.string.media_projection_action_text, this)
.setNeutralButton(android.R.string.cancel, this) .setNeutralButton(android.R.string.cancel, this);
.setOnCancelListener(this);
if (isPartialScreenSharingEnabled()) {
// This is a temporary entry point before we have a new permission dialog
// TODO(b/233183090): this activity should be redesigned to have a dropdown selector
dialogBuilder.setNegativeButton("App", this);
}
mDialog = dialogBuilder.create(); mDialog = dialogBuilder.create();
}
SystemUIDialog.registerDismissListener(mDialog); SystemUIDialog.registerDismissListener(mDialog);
SystemUIDialog.applyFlags(mDialog); SystemUIDialog.applyFlags(mDialog);
SystemUIDialog.setDialogSize(mDialog); SystemUIDialog.setDialogSize(mDialog);
mDialog.setOnCancelListener(this);
mDialog.create(); mDialog.create();
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true); mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
@@ -186,12 +194,17 @@ public class MediaProjectionPermissionActivity extends Activity
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
try {
if (which == AlertDialog.BUTTON_POSITIVE) { if (which == AlertDialog.BUTTON_POSITIVE) {
setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName)); grantMediaProjectionPermission(ENTIRE_SCREEN);
}
} }
if (isPartialScreenSharingEnabled() && which == AlertDialog.BUTTON_NEGATIVE) { private void grantMediaProjectionPermission(int screenShareMode) {
try {
if (screenShareMode == ENTIRE_SCREEN) {
setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName));
}
if (isPartialScreenSharingEnabled() && screenShareMode == SINGLE_APP) {
IMediaProjection projection = createProjection(mUid, mPackageName); IMediaProjection projection = createProjection(mUid, mPackageName);
final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class); final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class);
intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,

View File

@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles; package com.android.systemui.qs.tiles;
import android.app.Dialog;
import android.content.Intent; import android.content.Intent;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -43,7 +44,6 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -170,9 +170,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
getHost().collapsePanels(); getHost().collapsePanels();
}; };
ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
ActivityStarter.OnDismissAction dismissAction = () -> { ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromView) { if (shouldAnimateFromView) {
mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj( mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(

View File

@@ -0,0 +1,110 @@
/*
* 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.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.View
import android.view.ViewStub
import android.view.WindowManager
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import com.android.systemui.R
import com.android.systemui.statusbar.phone.SystemUIDialog
/** Base permission dialog for screen share and recording */
open class BaseScreenSharePermissionDialog(
context: Context?,
private val screenShareOptions: List<ScreenShareOption>,
private val appName: String?
) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
private lateinit var dialogTitle: TextView
private lateinit var startButton: TextView
private lateinit var warning: TextView
private lateinit var screenShareModeSpinner: Spinner
var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.apply {
addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
setGravity(Gravity.CENTER)
}
setContentView(R.layout.screen_share_dialog)
dialogTitle = findViewById(R.id.screen_share_dialog_title)
warning = findViewById(R.id.text_warning)
startButton = findViewById(R.id.button_start)
findViewById<TextView>(R.id.button_cancel).setOnClickListener { dismiss() }
initScreenShareOptions()
createOptionsView(getOptionsViewLayoutId())
}
protected fun initScreenShareOptions() {
selectedScreenShareOption = screenShareOptions.first()
warning.text = warningText
initScreenShareSpinner()
}
private val warningText: String
get() = context.getString(selectedScreenShareOption.warningText, appName)
private fun initScreenShareSpinner() {
val options = screenShareOptions.map { context.getString(it.spinnerText) }.toTypedArray()
val adapter =
ArrayAdapter(context.applicationContext, android.R.layout.simple_spinner_item, options)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
screenShareModeSpinner = findViewById(R.id.screen_share_mode_spinner)
screenShareModeSpinner.adapter = adapter
screenShareModeSpinner.onItemSelectedListener = this
}
override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
selectedScreenShareOption = screenShareOptions[pos]
warning.text = warningText
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
/** Protected methods for the text updates & functionality */
protected fun setDialogTitle(@StringRes stringId: Int) {
val title = context.getString(stringId, appName)
dialogTitle.text = title
}
protected fun setStartButtonText(@StringRes stringId: Int) {
startButton.setText(stringId)
}
protected fun setStartButtonOnClickListener(listener: View.OnClickListener?) {
startButton.setOnClickListener(listener)
}
// Create additional options that is shown under the share mode spinner
// Eg. the audio and tap toggles in SysUI Recorder
@LayoutRes protected open fun getOptionsViewLayoutId(): Int? = null
private fun createOptionsView(@LayoutRes layoutId: Int?) {
if (layoutId == null) return
val stub = findViewById<View>(R.id.options_stub) as ViewStub
stub.layoutResource = layoutId
stub.inflate()
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.content.Context
import android.os.Bundle
import com.android.systemui.R
/** Dialog to select screen recording options */
class MediaProjectionPermissionDialog(
context: Context?,
private val onStartRecordingClicked: Runnable,
appName: String?
) : BaseScreenSharePermissionDialog(context, createOptionList(), appName) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setDialogTitle(R.string.media_projection_permission_dialog_title)
setStartButtonText(R.string.media_projection_permission_dialog_continue)
setStartButtonOnClickListener {
// Note that it is important to run this callback before dismissing, so that the
// callback can disable the dialog exit animation if it wants to.
onStartRecordingClicked.run()
dismiss()
}
}
companion object {
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
SINGLE_APP,
R.string.media_projection_permission_dialog_option_single_app,
R.string.media_projection_permission_dialog_warning_single_app
),
ScreenShareOption(
ENTIRE_SCREEN,
R.string.media_projection_permission_dialog_option_entire_screen,
R.string.media_projection_permission_dialog_warning_entire_screen
)
)
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.screenrecord; package com.android.systemui.screenrecord;
import android.app.Dialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@@ -33,6 +34,7 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider; import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.statusbar.policy.CallbackController;
@@ -97,11 +99,15 @@ public class RecordingController
} }
/** Create a dialog to show screen recording options to the user. */ /** Create a dialog to show screen recording options to the user. */
public ScreenRecordDialog createScreenRecordDialog(Context context, FeatureFlags flags, public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
DialogLaunchAnimator dialogLaunchAnimator, ActivityStarter activityStarter, DialogLaunchAnimator dialogLaunchAnimator,
ActivityStarter activityStarter,
@Nullable Runnable onStartRecordingClicked) { @Nullable Runnable onStartRecordingClicked) {
return new ScreenRecordDialog(context, this, activityStarter, mUserContextProvider, return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
flags, dialogLaunchAnimator, onStartRecordingClicked); ? new ScreenRecordPermissionDialog(context, this, activityStarter,
dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked)
: new ScreenRecordDialog(context, this, activityStarter,
mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
} }
/** /**

View File

@@ -0,0 +1,173 @@
/*
* 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.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.View
import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Switch
import androidx.annotation.LayoutRes
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.media.MediaProjectionCaptureTarget
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserContextProvider
/** Dialog to select screen recording options */
class ScreenRecordPermissionDialog(
context: Context?,
private val controller: RecordingController,
private val activityStarter: ActivityStarter,
private val dialogLaunchAnimator: DialogLaunchAnimator,
private val userContextProvider: UserContextProvider,
private val onStartRecordingClicked: Runnable?
) : BaseScreenSharePermissionDialog(context, createOptionList(), null) {
private lateinit var tapsSwitch: Switch
private lateinit var audioSwitch: Switch
private lateinit var options: Spinner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setDialogTitle(R.string.screenrecord_start_label)
setStartButtonText(R.string.screenrecord_start_recording)
setStartButtonOnClickListener { v: View? ->
onStartRecordingClicked?.run()
if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
requestScreenCapture(/* captureTarget= */ null)
}
if (selectedScreenShareOption.mode == SINGLE_APP) {
val intent = Intent(context, MediaProjectionAppSelectorActivity::class.java)
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(
MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
CaptureTargetResultReceiver()
)
val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
if (animationController == null) {
dismiss()
}
activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
}
dismiss()
}
initRecordOptionsView()
}
@LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
private fun initRecordOptionsView() {
audioSwitch = findViewById(R.id.screenrecord_audio_switch)
tapsSwitch = findViewById(R.id.screenrecord_taps_switch)
options = findViewById(R.id.screen_recording_options)
val a: ArrayAdapter<*> =
ScreenRecordingAdapter(context, android.R.layout.simple_spinner_dropdown_item, MODES)
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
options.adapter = a
options.setOnItemClickListenerInt(
OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
audioSwitch.isChecked = true
}
)
}
/**
* 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 fun requestScreenCapture(captureTarget: MediaProjectionCaptureTarget?) {
val userContext = userContextProvider.userContext
val showTaps = tapsSwitch.isChecked
val audioMode =
if (audioSwitch.isChecked) options.selectedItem as ScreenRecordingAudioSource
else ScreenRecordingAudioSource.NONE
val startIntent =
PendingIntent.getForegroundService(
userContext,
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
userContext,
Activity.RESULT_OK,
audioMode.ordinal,
showTaps,
captureTarget
),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val stopIntent =
PendingIntent.getService(
userContext,
RecordingService.REQUEST_CODE,
RecordingService.getStopIntent(userContext),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
controller.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent)
}
private inner class CaptureTargetResultReceiver() :
ResultReceiver(Handler(Looper.getMainLooper())) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
if (resultCode == Activity.RESULT_OK) {
val captureTarget =
resultData.getParcelable(
MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET,
MediaProjectionCaptureTarget::class.java
)
// Start recording of the selected target
requestScreenCapture(captureTarget)
}
}
}
companion object {
private val MODES =
listOf(
ScreenRecordingAudioSource.INTERNAL,
ScreenRecordingAudioSource.MIC,
ScreenRecordingAudioSource.MIC_AND_INTERNAL
)
private const val DELAY_MS: Long = 3000
private const val INTERVAL_MS: Long = 1000
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
SINGLE_APP,
R.string.screenrecord_option_single_app,
R.string.screenrecord_warning_single_app
),
ScreenShareOption(
ENTIRE_SCREEN,
R.string.screenrecord_option_entire_screen,
R.string.screenrecord_warning_entire_screen
)
)
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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 androidx.annotation.IntDef
import androidx.annotation.StringRes
import kotlin.annotation.Retention
@Retention(AnnotationRetention.SOURCE)
@IntDef(SINGLE_APP, ENTIRE_SCREEN)
annotation class ScreenShareMode
const val SINGLE_APP = 0
const val ENTIRE_SCREEN = 1
class ScreenShareOption(
@ScreenShareMode val mode: Int,
@StringRes val spinnerText: Int,
@StringRes val warningText: Int
)