diff options
3 files changed, 126 insertions, 68 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index f004c3a8916f..6c5337418d02 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -187,44 +187,17 @@ public class MediaProjectionPermissionActivity extends Activity } } - TextPaint paint = new TextPaint(); - paint.setTextSize(42); - CharSequence dialogText = null; CharSequence dialogTitle = null; - String appName = null; - if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) { + + final String appName = extractAppName(aInfo, packageManager); + final boolean hasCastingCapabilities = + Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName); + + if (hasCastingCapabilities) { dialogText = getString(R.string.media_projection_sys_service_dialog_warning); dialogTitle = getString(R.string.media_projection_sys_service_dialog_title); } else { - String label = aInfo.loadLabel(packageManager).toString(); - - // If the label contains new line characters it may push the security - // message below the fold of the dialog. Labels shouldn't have new line - // characters anyways, so just truncate the message the first time one - // is seen. - final int labelLength = label.length(); - int offset = 0; - while (offset < labelLength) { - final int codePoint = label.codePointAt(offset); - final int type = Character.getType(codePoint); - if (type == Character.LINE_SEPARATOR - || type == Character.CONTROL - || type == Character.PARAGRAPH_SEPARATOR) { - label = label.substring(0, offset) + ELLIPSIS; - break; - } - offset += Character.charCount(codePoint); - } - - if (label.isEmpty()) { - label = mPackageName; - } - - String unsanitizedAppName = TextUtils.ellipsize(label, - paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString(); - appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName); - String actionText = getString(R.string.media_projection_dialog_warning, appName); SpannableString message = new SpannableString(actionText); @@ -255,6 +228,7 @@ public class MediaProjectionPermissionActivity extends Activity grantMediaProjectionPermission(selectedOption.getMode()); }, () -> finish(RECORD_CANCEL, /* projection= */ null), + hasCastingCapabilities, appName, overrideDisableSingleAppOption, mUid, @@ -289,6 +263,47 @@ public class MediaProjectionPermissionActivity extends Activity } } + private String extractAppName(ApplicationInfo applicationInfo, PackageManager packageManager) { + String label = applicationInfo.loadLabel(packageManager).toString(); + + // If the label contains new line characters it may push the security + // message below the fold of the dialog. Labels shouldn't have new line + // characters anyways, so just truncate the message the first time one + // is seen. + final int labelLength = label.length(); + int offset = 0; + while (offset < labelLength) { + final int codePoint = label.codePointAt(offset); + final int type = Character.getType(codePoint); + if (type == Character.LINE_SEPARATOR + || type == Character.CONTROL + || type == Character.PARAGRAPH_SEPARATOR) { + label = label.substring(0, offset) + ELLIPSIS; + break; + } + offset += Character.charCount(codePoint); + } + + if (label.isEmpty()) { + label = mPackageName; + } + + TextPaint paint = new TextPaint(); + paint.setTextSize(42); + + String unsanitizedAppName = TextUtils.ellipsize(label, + paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString(); + String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName); + + // Have app name be the package name as a default fallback, if specific app name can't be + // extracted + if (appName == null || appName.isEmpty()) { + return mPackageName; + } + + return appName; + } + @Override protected void onDestroy() { super.onDestroy(); diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt index 9ce8070131fa..6d1a4587d089 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt @@ -29,13 +29,20 @@ class MediaProjectionPermissionDialogDelegate( mediaProjectionConfig: MediaProjectionConfig?, private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>, private val onCancelClicked: Runnable, - private val appName: String?, + private val hasCastingCapabilities: Boolean, + appName: String, forceShowPartialScreenshare: Boolean, hostUid: Int, mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, ) : BaseMediaProjectionPermissionDialogDelegate<AlertDialog>( - createOptionList(context, appName, mediaProjectionConfig, forceShowPartialScreenshare), + createOptionList( + context, + appName, + hasCastingCapabilities, + mediaProjectionConfig, + forceShowPartialScreenshare + ), appName, hostUid, mediaProjectionMetricsLogger @@ -43,7 +50,7 @@ class MediaProjectionPermissionDialogDelegate( override fun onCreate(dialog: AlertDialog, savedInstanceState: Bundle?) { super.onCreate(dialog, savedInstanceState) // TODO(b/270018943): Handle the case of System sharing (not recording nor casting) - if (appName == null) { + if (hasCastingCapabilities) { setDialogTitle(R.string.media_projection_entry_cast_permission_dialog_title) setStartButtonText(R.string.media_projection_entry_cast_permission_dialog_continue) } else { @@ -65,30 +72,29 @@ class MediaProjectionPermissionDialogDelegate( companion object { private fun createOptionList( context: Context, - appName: String?, + appName: String, + hasCastingCapabilities: Boolean, mediaProjectionConfig: MediaProjectionConfig?, overrideDisableSingleAppOption: Boolean = false, ): List<ScreenShareOption> { val singleAppWarningText = - if (appName == null) { + if (hasCastingCapabilities) { R.string.media_projection_entry_cast_permission_dialog_warning_single_app } else { R.string.media_projection_entry_app_permission_dialog_warning_single_app } val entireScreenWarningText = - if (appName == null) { + if (hasCastingCapabilities) { R.string.media_projection_entry_cast_permission_dialog_warning_entire_screen } else { R.string.media_projection_entry_app_permission_dialog_warning_entire_screen } - // The single app option should only be disabled if there is an app name provided, - // the client has setup a MediaProjection with - // MediaProjectionConfig#createConfigForDefaultDisplay, AND it hasn't been overridden by - // the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override. + // The single app option should only be disabled if the client has setup a + // MediaProjection with MediaProjectionConfig#createConfigForDefaultDisplay AND + // it hasn't been overridden by the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override. val singleAppOptionDisabled = - appName != null && - !overrideDisableSingleAppOption && + !overrideDisableSingleAppOption && mediaProjectionConfig?.regionToCapture == MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt index d183c7370713..7dd802878674 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt @@ -47,13 +47,7 @@ class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() { private lateinit var dialog: AlertDialog private val flags = mock<FeatureFlagsClassic>() - private val onStartRecordingClicked = mock<Runnable>() - private val mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>() - - private val mediaProjectionConfig: MediaProjectionConfig = - MediaProjectionConfig.createConfigForDefaultDisplay() - private val appName: String = "testApp" - private val hostUid: Int = 12345 + private val appName = "Test App" private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen @@ -73,16 +67,33 @@ class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() { } @Test - fun showDialog_forceShowPartialScreenShareFalse() { - // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and - // overrideDisableSingleAppOption = false - val overrideDisableSingleAppOption = false - setUpAndShowDialog(overrideDisableSingleAppOption) + fun showDefaultDialog() { + setUpAndShowDialog() val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options) val secondOptionText = spinner.adapter .getDropDownView(1, null, spinner) + .findViewById<TextView>(android.R.id.text1) + ?.text + + // check that the first option is single app and enabled + assertEquals(context.getString(resIdSingleApp), spinner.selectedItem) + + // check that the second option is full screen and enabled + assertEquals(context.getString(resIdFullScreen), secondOptionText) + } + + @Test + fun showDialog_disableSingleApp() { + setUpAndShowDialog( + mediaProjectionConfig = MediaProjectionConfig.createConfigForDefaultDisplay() + ) + + val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options) + val secondOptionWarningText = + spinner.adapter + .getDropDownView(1, null, spinner) .findViewById<TextView>(android.R.id.text2) ?.text @@ -90,15 +101,15 @@ class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() { assertEquals(context.getString(resIdFullScreen), spinner.selectedItem) // check that the second option is single app and disabled - assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionText) + assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionWarningText) } @Test - fun showDialog_forceShowPartialScreenShareTrue() { - // Set up dialog with MediaProjectionConfig.createConfigForDefaultDisplay() and - // overrideDisableSingleAppOption = true - val overrideDisableSingleAppOption = true - setUpAndShowDialog(overrideDisableSingleAppOption) + fun showDialog_disableSingleApp_forceShowPartialScreenShareTrue() { + setUpAndShowDialog( + mediaProjectionConfig = MediaProjectionConfig.createConfigForDefaultDisplay(), + overrideDisableSingleAppOption = true + ) val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options) val secondOptionText = @@ -114,17 +125,43 @@ class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() { assertEquals(context.getString(resIdFullScreen), secondOptionText) } - private fun setUpAndShowDialog(overrideDisableSingleAppOption: Boolean) { + @Test + fun showDialog_disableSingleApp_hasCastingCapabilities() { + setUpAndShowDialog( + mediaProjectionConfig = MediaProjectionConfig.createConfigForDefaultDisplay(), + hasCastingCapabilities = true + ) + + val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options) + val secondOptionWarningText = + spinner.adapter + .getDropDownView(1, null, spinner) + .findViewById<TextView>(android.R.id.text2) + ?.text + + // check that the first option is full screen and enabled + assertEquals(context.getString(resIdFullScreen), spinner.selectedItem) + + // check that the second option is single app and disabled + assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionWarningText) + } + + private fun setUpAndShowDialog( + mediaProjectionConfig: MediaProjectionConfig? = null, + overrideDisableSingleAppOption: Boolean = false, + hasCastingCapabilities: Boolean = false, + ) { val delegate = MediaProjectionPermissionDialogDelegate( context, mediaProjectionConfig, - {}, - onStartRecordingClicked, + onStartRecordingClicked = {}, + onCancelClicked = {}, + hasCastingCapabilities, appName, overrideDisableSingleAppOption, - hostUid, - mediaProjectionMetricsLogger + hostUid = 12345, + mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>() ) dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate) |