diff options
| author | 2022-11-09 11:30:26 +0000 | |
|---|---|---|
| committer | 2022-11-09 11:30:26 +0000 | |
| commit | dccb3586684b1a7f11dfbfc1715d6cf09373523c (patch) | |
| tree | 6f64f0e404e3ff9eb3bbeef042f2f69ea736063d | |
| parent | abee62b65c510c4ca289d94992a233fd49bd41a0 (diff) | |
| parent | c3b0b68436ce16dd6102c542df6781f4c14d7b24 (diff) | |
Merge "[Partial Screensharing] Permission Dialog initial version" into tm-qpr-dev
9 files changed, 597 insertions, 26 deletions
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml new file mode 100644 index 000000000000..a93691434bc0 --- /dev/null +++ b/packages/SystemUI/res/layout/screen_record_options.xml @@ -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>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml new file mode 100644 index 000000000000..ac46cdb1be24 --- /dev/null +++ b/packages/SystemUI/res/layout/screen_share_dialog.xml @@ -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>
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java index 397bffcaa64c..22f91f359de8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java @@ -18,6 +18,9 @@ package com.android.systemui.media; 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.AlertDialog; import android.content.DialogInterface; @@ -44,6 +47,8 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.flags.FeatureFlags; 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.util.Utils; @@ -102,7 +107,9 @@ public class MediaProjectionPermissionActivity extends Activity CharSequence dialogText = null; CharSequence dialogTitle = null; + String appName = null; if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) { + // TODO(b/253438807): handle special app name dialogText = getString(R.string.media_projection_dialog_service_text); dialogTitle = getString(R.string.media_projection_dialog_service_title); } else { @@ -132,7 +139,7 @@ public class MediaProjectionPermissionActivity extends Activity String unsanitizedAppName = TextUtils.ellipsize(label, 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); SpannableString message = new SpannableString(actionText); @@ -146,27 +153,28 @@ public class MediaProjectionPermissionActivity extends Activity dialogTitle = getString(R.string.media_projection_dialog_title, appName); } - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, - R.style.Theme_SystemUI_Dialog) - .setTitle(dialogTitle) - .setIcon(R.drawable.ic_media_projection_permission) - .setMessage(dialogText) - .setPositiveButton(R.string.media_projection_action_text, 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 = new MediaProjectionPermissionDialog(this, () -> { + ScreenShareOption selectedOption = + ((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption(); + grantMediaProjectionPermission(selectedOption.getMode()); + }, appName); + } else { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, + R.style.Theme_SystemUI_Dialog) + .setTitle(dialogTitle) + .setIcon(R.drawable.ic_media_projection_permission) + .setMessage(dialogText) + .setPositiveButton(R.string.media_projection_action_text, this) + .setNeutralButton(android.R.string.cancel, this); + mDialog = dialogBuilder.create(); } - mDialog = dialogBuilder.create(); - SystemUIDialog.registerDismissListener(mDialog); SystemUIDialog.applyFlags(mDialog); SystemUIDialog.setDialogSize(mDialog); + mDialog.setOnCancelListener(this); mDialog.create(); mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true); @@ -186,12 +194,17 @@ public class MediaProjectionPermissionActivity extends Activity @Override public void onClick(DialogInterface dialog, int which) { + if (which == AlertDialog.BUTTON_POSITIVE) { + grantMediaProjectionPermission(ENTIRE_SCREEN); + } + } + + private void grantMediaProjectionPermission(int screenShareMode) { try { - if (which == AlertDialog.BUTTON_POSITIVE) { + if (screenShareMode == ENTIRE_SCREEN) { setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName)); } - - if (isPartialScreenSharingEnabled() && which == AlertDialog.BUTTON_NEGATIVE) { + if (isPartialScreenSharingEnabled() && screenShareMode == SINGLE_APP) { IMediaProjection projection = createProjection(mUid, mPackageName); final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class); intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, 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 f63f0444210b..64a8a146031b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import android.app.Dialog; import android.content.Intent; import android.os.Handler; 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.tileimpl.QSTileImpl; import com.android.systemui.screenrecord.RecordingController; -import com.android.systemui.screenrecord.ScreenRecordDialog; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -170,9 +170,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); getHost().collapsePanels(); }; - ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext, mFlags, - mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked); + Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags, + mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked); ActivityStarter.OnDismissAction dismissAction = () -> { if (shouldAnimateFromView) { mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj( diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt new file mode 100644 index 000000000000..f4d59a8dd6a6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt @@ -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() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt new file mode 100644 index 000000000000..15b0bc4a356a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt @@ -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 + ) + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index 1083f22164d9..ce4e0ecee914 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java @@ -16,6 +16,7 @@ package com.android.systemui.screenrecord; +import android.app.Dialog; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -33,6 +34,7 @@ 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.flags.Flags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserContextProvider; 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. */ - public ScreenRecordDialog createScreenRecordDialog(Context context, FeatureFlags flags, - DialogLaunchAnimator dialogLaunchAnimator, ActivityStarter activityStarter, - @Nullable Runnable onStartRecordingClicked) { - return new ScreenRecordDialog(context, this, activityStarter, mUserContextProvider, - flags, dialogLaunchAnimator, onStartRecordingClicked); + public Dialog createScreenRecordDialog(Context context, FeatureFlags flags, + DialogLaunchAnimator dialogLaunchAnimator, + ActivityStarter activityStarter, + @Nullable Runnable onStartRecordingClicked) { + return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING) + ? new ScreenRecordPermissionDialog(context, this, activityStarter, + dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked) + : new ScreenRecordDialog(context, this, activityStarter, + mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt new file mode 100644 index 000000000000..cffd28f9fc96 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt @@ -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 + ) + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt new file mode 100644 index 000000000000..914d29a52b53 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt @@ -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 +) |