diff --git a/core/api/current.txt b/core/api/current.txt index aedad0d748f88..877e2d6b54b4b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -25468,8 +25468,17 @@ package android.media.projection { method public void onStop(); } + public final class MediaProjectionConfig implements android.os.Parcelable { + method @NonNull public static android.media.projection.MediaProjectionConfig createConfigForDisplay(@IntRange(from=android.view.Display.DEFAULT_DISPLAY, to=android.view.Display.DEFAULT_DISPLAY) int); + method @NonNull public static android.media.projection.MediaProjectionConfig createConfigForUserChoice(); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public final class MediaProjectionManager { - method public android.content.Intent createScreenCaptureIntent(); + method @NonNull public android.content.Intent createScreenCaptureIntent(); + method @NonNull public android.content.Intent createScreenCaptureIntent(@NonNull android.media.projection.MediaProjectionConfig); method public android.media.projection.MediaProjection getMediaProjection(int, @NonNull android.content.Intent); } diff --git a/media/java/android/media/projection/MediaProjectionConfig.aidl b/media/java/android/media/projection/MediaProjectionConfig.aidl new file mode 100644 index 0000000000000..f78385f43d3d4 --- /dev/null +++ b/media/java/android/media/projection/MediaProjectionConfig.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.media.projection; + +parcelable MediaProjectionConfig; diff --git a/media/java/android/media/projection/MediaProjectionConfig.java b/media/java/android/media/projection/MediaProjectionConfig.java new file mode 100644 index 0000000000000..29afaa6f4c436 --- /dev/null +++ b/media/java/android/media/projection/MediaProjectionConfig.java @@ -0,0 +1,293 @@ +/* + * 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 android.media.projection; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; + +import com.android.internal.util.AnnotationValidations; +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; + +/** + * Configure the {@link MediaProjection} session requested from + * {@link MediaProjectionManager#createScreenCaptureIntent(MediaProjectionConfig)}. + */ +@DataClass( + genEqualsHashCode = true, + genAidl = true, + genSetters = false, + genConstructor = false, + genBuilder = false, + genToString = false, + genHiddenConstDefs = true, + genHiddenGetters = true, + genConstDefs = false +) +public final class MediaProjectionConfig implements Parcelable { + + /** + * The user, rather than the host app, determines which region of the display to capture. + * @hide + */ + public static final int CAPTURE_REGION_USER_CHOICE = 0; + + /** + * The host app specifies a particular display to capture. + * @hide + */ + public static final int CAPTURE_REGION_FIXED_DISPLAY = 1; + + /** @hide */ + @IntDef(prefix = "CAPTURE_REGION_", value = { + CAPTURE_REGION_USER_CHOICE, + CAPTURE_REGION_FIXED_DISPLAY + }) + @Retention(SOURCE) + public @interface CaptureRegion { + } + + /** + * The particular display to capture. Only used when {@link #getRegionToCapture()} is + * {@link #CAPTURE_REGION_FIXED_DISPLAY}; ignored otherwise. + * + * Only supports values of {@link android.view.Display#DEFAULT_DISPLAY}. + */ + @IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY) + private int mDisplayToCapture; + + /** + * The region to capture. Defaults to the user's choice. + */ + @CaptureRegion + private int mRegionToCapture = CAPTURE_REGION_USER_CHOICE; + + /** + * Default instance, with region set to the user's choice. + */ + private MediaProjectionConfig() { + } + + /** + * Customized instance, with region set to the provided value. + */ + private MediaProjectionConfig(@CaptureRegion int captureRegion) { + mRegionToCapture = captureRegion; + } + + /** + * Returns an instance which restricts the user to capturing a particular display. + * + * @param displayId The id of the display to capture. Only supports values of + * {@link android.view.Display#DEFAULT_DISPLAY}. + * @throws IllegalArgumentException If the given {@code displayId} is outside the range of + * supported values. + */ + @NonNull + public static MediaProjectionConfig createConfigForDisplay( + @IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY) int displayId) { + if (displayId != DEFAULT_DISPLAY) { + throw new IllegalArgumentException( + "A config for capturing the non-default display is not supported; requested " + + "display id " + + displayId); + } + MediaProjectionConfig config = new MediaProjectionConfig(CAPTURE_REGION_FIXED_DISPLAY); + config.mDisplayToCapture = displayId; + return config; + } + + /** + * Returns an instance which allows the user to decide which region is captured. The consent + * dialog presents the user with all possible options. If the user selects display capture, + * then only the {@link android.view.Display#DEFAULT_DISPLAY} is supported. + * + *

+ * When passed in to + * {@link MediaProjectionManager#createScreenCaptureIntent(MediaProjectionConfig)}, the consent + * dialog shown to the user will be the same as if just + * {@link MediaProjectionManager#createScreenCaptureIntent()} was invoked. + *

+ */ + @NonNull + public static MediaProjectionConfig createConfigForUserChoice() { + return new MediaProjectionConfig(CAPTURE_REGION_USER_CHOICE); + } + + /** + * Returns string representation of the captured region. + */ + @NonNull + private static String captureRegionToString(int value) { + switch (value) { + case CAPTURE_REGION_USER_CHOICE: + return "CAPTURE_REGION_USERS_CHOICE"; + case CAPTURE_REGION_FIXED_DISPLAY: + return "CAPTURE_REGION_GIVEN_DISPLAY"; + default: + return Integer.toHexString(value); + } + } + + @Override + public String toString() { + return "MediaProjectionConfig { " + + "displayToCapture = " + mDisplayToCapture + ", " + + "regionToCapture = " + captureRegionToString(mRegionToCapture) + + " }"; + } + + + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/projection/MediaProjectionConfig.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * The particular display to capture. Only used when {@link #getRegionToCapture()} is + * {@link #CAPTURE_REGION_FIXED_DISPLAY}; ignored otherwise. + * + * Only supports values of {@link android.view.Display#DEFAULT_DISPLAY}. + * + * @hide + */ + @DataClass.Generated.Member + public @IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY) int getDisplayToCapture() { + return mDisplayToCapture; + } + + /** + * The region to capture. Defaults to the user's choice. + * + * @hide + */ + @DataClass.Generated.Member + public @CaptureRegion int getRegionToCapture() { + return mRegionToCapture; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(MediaProjectionConfig other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + MediaProjectionConfig that = (MediaProjectionConfig) o; + //noinspection PointlessBooleanExpression + return true + && mDisplayToCapture == that.mDisplayToCapture + && mRegionToCapture == that.mRegionToCapture; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mDisplayToCapture; + _hash = 31 * _hash + mRegionToCapture; + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mDisplayToCapture); + dest.writeInt(mRegionToCapture); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ MediaProjectionConfig(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int displayToCapture = in.readInt(); + int regionToCapture = in.readInt(); + + this.mDisplayToCapture = displayToCapture; + AnnotationValidations.validate( + IntRange.class, null, mDisplayToCapture, + "from", DEFAULT_DISPLAY, + "to", DEFAULT_DISPLAY); + this.mRegionToCapture = regionToCapture; + AnnotationValidations.validate( + CaptureRegion.class, null, mRegionToCapture); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public MediaProjectionConfig[] newArray(int size) { + return new MediaProjectionConfig[size]; + } + + @Override + public MediaProjectionConfig createFromParcel(@NonNull android.os.Parcel in) { + return new MediaProjectionConfig(in); + } + }; + + @DataClass.Generated( + time = 1671030124845L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/media/java/android/media/projection/MediaProjectionConfig.java", + inputSignatures = "public static final int CAPTURE_REGION_USER_CHOICE\npublic static final int CAPTURE_REGION_FIXED_DISPLAY\nprivate @android.annotation.IntRange int mDisplayToCapture\nprivate @android.media.projection.MediaProjectionConfig.CaptureRegion int mRegionToCapture\npublic static @android.annotation.NonNull android.media.projection.MediaProjectionConfig createConfigForDisplay(int)\npublic static @android.annotation.NonNull android.media.projection.MediaProjectionConfig createConfigForUserChoice()\nprivate static @android.annotation.NonNull java.lang.String captureRegionToString(int)\npublic @java.lang.Override java.lang.String toString()\nclass MediaProjectionConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genSetters=false, genConstructor=false, genBuilder=false, genToString=false, genHiddenConstDefs=true, genHiddenGetters=true, genConstDefs=false)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java index b3bd980451648..a4215e68bfef8 100644 --- a/media/java/android/media/projection/MediaProjectionManager.java +++ b/media/java/android/media/projection/MediaProjectionManager.java @@ -38,6 +38,13 @@ import java.util.Map; @SystemService(Context.MEDIA_PROJECTION_SERVICE) public final class MediaProjectionManager { private static final String TAG = "MediaProjectionManager"; + + /** + * Intent extra to customize the permission dialog based on the host app's preferences. + * @hide + */ + public static final String EXTRA_MEDIA_PROJECTION_CONFIG = + "android.media.projection.extra.EXTRA_MEDIA_PROJECTION_CONFIG"; /** @hide */ public static final String EXTRA_APP_TOKEN = "android.media.projection.extra.EXTRA_APP_TOKEN"; /** @hide */ @@ -64,11 +71,13 @@ public final class MediaProjectionManager { } /** - * Returns an Intent that must be passed to startActivityForResult() - * in order to start screen capture. The activity will prompt - * the user whether to allow screen capture. The result of this - * activity should be passed to getMediaProjection. + * Returns an {@link Intent} that must be passed to + * {@link Activity#startActivityForResult(Intent, int)} (or similar) in order to start screen + * capture. The activity will prompt the user whether to allow screen capture. The result of + * this activity (received by overriding {@link Activity#onActivityResult(int, int, Intent)}) + * should be passed to {@link #getMediaProjection(int, Intent)}. */ + @NonNull public Intent createScreenCaptureIntent() { Intent i = new Intent(); final ComponentName mediaProjectionPermissionDialogComponent = @@ -79,6 +88,49 @@ public final class MediaProjectionManager { return i; } + /** + * Returns an {@link Intent} that must be passed to + * {@link Activity#startActivityForResult(Intent, int)} (or similar) in order to start screen + * capture. Customizes the activity and resulting {@link MediaProjection} session based up + * the provided {@code config}. The activity will prompt the user whether to allow screen + * capture. The result of this activity (received by overriding + * {@link Activity#onActivityResult(int, int, Intent)}) should be passed to + * {@link #getMediaProjection(int, Intent)}. + * + *

+ * If {@link MediaProjectionConfig} was created from: + *

  • + *
      + * {@link MediaProjectionConfig#createConfigForDisplay(int)}, then creates an + * {@link Intent} for capturing this particular display. The activity limits the user's + * choice to just the display specified. + *
    + *
      + * {@link MediaProjectionConfig#createConfigForUserChoice()}, then creates an + * {@link Intent} for deferring which region to capture to the user. This gives the + * user the same behaviour as calling {@link #createScreenCaptureIntent()}. The + * activity gives the user the choice between + * {@link android.view.Display#DEFAULT_DISPLAY}, or a different region. + *
    + *
  • + * + * @param config Customization for the {@link MediaProjection} that this {@link Intent} requests + * the user's consent for. + * @return An {@link Intent} requesting the user's consent, specialized based upon the given + * configuration. + */ + @NonNull + public Intent createScreenCaptureIntent(@NonNull MediaProjectionConfig config) { + Intent i = new Intent(); + final ComponentName mediaProjectionPermissionDialogComponent = + ComponentName.unflattenFromString(mContext.getResources() + .getString(com.android.internal.R.string + .config_mediaProjectionPermissionDialogComponent)); + i.setComponent(mediaProjectionPermissionDialogComponent); + i.putExtra(EXTRA_MEDIA_PROJECTION_CONFIG, config); + return i; + } + /** * Retrieves the {@link MediaProjection} obtained from a successful screen * capture request. The result code and data from the request are provided diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp new file mode 100644 index 0000000000000..08d950128ce25 --- /dev/null +++ b/media/tests/projection/Android.bp @@ -0,0 +1,46 @@ +//######################################################################## +// Build MediaProjectionTests package +//######################################################################## + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "MediaProjectionTests", + + srcs: ["**/*.java"], + + libs: [ + "android.test.base", + "android.test.mock", + "android.test.runner", + ], + + static_libs: [ + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "platform-test-annotations", + "testng", + "truth-prebuilt", + ], + + // Needed for mockito-target-extended-minus-junit4 + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + test_suites: ["device-tests"], + + platform_apis: true, + + certificate: "platform", +} diff --git a/media/tests/projection/AndroidManifest.xml b/media/tests/projection/AndroidManifest.xml new file mode 100644 index 0000000000000..62f148cfdde12 --- /dev/null +++ b/media/tests/projection/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/media/tests/projection/AndroidTest.xml b/media/tests/projection/AndroidTest.xml new file mode 100644 index 0000000000000..f64930a0eb3fd --- /dev/null +++ b/media/tests/projection/AndroidTest.xml @@ -0,0 +1,32 @@ + + + + diff --git a/media/tests/projection/TEST_MAPPING b/media/tests/projection/TEST_MAPPING new file mode 100644 index 0000000000000..ddb68af107344 --- /dev/null +++ b/media/tests/projection/TEST_MAPPING @@ -0,0 +1,13 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + {"include-filter": "android.media.projection.mediaprojectiontests"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, + {"exclude-annotation": "androidx.test.filters.FlakyTest"}, + {"exclude-annotation": "org.junit.Ignore"} + ] + } + ] +} diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionConfigTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionConfigTest.java new file mode 100644 index 0000000000000..a30f2e3c7c885 --- /dev/null +++ b/media/tests/projection/src/android/media/projection/MediaProjectionConfigTest.java @@ -0,0 +1,84 @@ +/* + * 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 android.media.projection; + +import static android.media.projection.MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY; +import static android.media.projection.MediaProjectionConfig.CAPTURE_REGION_USER_CHOICE; +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; + +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the {@link MediaProjectionConfig} class. + * + * Build/Install/Run: + * atest MediaProjectionTests:MediaProjectionConfigTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class MediaProjectionConfigTest { + private static final MediaProjectionConfig DISPLAY_CONFIG = + MediaProjectionConfig.createConfigForDisplay(DEFAULT_DISPLAY); + private static final MediaProjectionConfig USERS_CHOICE_CONFIG = + MediaProjectionConfig.createConfigForUserChoice(); + + @Test + public void testParcelable() { + Parcel parcel = Parcel.obtain(); + DISPLAY_CONFIG.writeToParcel(parcel, 0 /* flags */); + parcel.setDataPosition(0); + MediaProjectionConfig config = MediaProjectionConfig.CREATOR.createFromParcel(parcel); + assertThat(DISPLAY_CONFIG).isEqualTo(config); + parcel.recycle(); + } + + @Test + public void testCreateDisplayConfig() { + assertThrows(IllegalArgumentException.class, + () -> MediaProjectionConfig.createConfigForDisplay(-1)); + assertThrows(IllegalArgumentException.class, + () -> MediaProjectionConfig.createConfigForDisplay(DEFAULT_DISPLAY + 1)); + assertThat(DISPLAY_CONFIG.getRegionToCapture()).isEqualTo(CAPTURE_REGION_FIXED_DISPLAY); + assertThat(DISPLAY_CONFIG.getDisplayToCapture()).isEqualTo(DEFAULT_DISPLAY); + } + + @Test + public void testCreateUsersChoiceConfig() { + assertThat(USERS_CHOICE_CONFIG.getRegionToCapture()).isEqualTo(CAPTURE_REGION_USER_CHOICE); + } + + @Test + public void testEquals() { + assertThat(MediaProjectionConfig.createConfigForUserChoice()).isEqualTo( + USERS_CHOICE_CONFIG); + assertThat(DISPLAY_CONFIG).isNotEqualTo(USERS_CHOICE_CONFIG); + assertThat(MediaProjectionConfig.createConfigForDisplay(DEFAULT_DISPLAY)).isEqualTo( + DISPLAY_CONFIG); + } +} diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionManagerTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionManagerTest.java new file mode 100644 index 0000000000000..a3e49088ac4ad --- /dev/null +++ b/media/tests/projection/src/android/media/projection/MediaProjectionManagerTest.java @@ -0,0 +1,125 @@ +/* + * 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 android.media.projection; + +import static android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION_CONFIG; +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.LENIENT; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.platform.test.annotations.Presubmit; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoSession; + +/** + * Tests for the {@link MediaProjectionManager} class. + * + * Build/Install/Run: + * atest MediaProjectionTests:MediaProjectionManagerTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class MediaProjectionManagerTest { + private MediaProjectionManager mMediaProjectionManager; + private Context mContext; + private MockitoSession mMockingSession; + private static final MediaProjectionConfig DISPLAY_CONFIG = + MediaProjectionConfig.createConfigForDisplay(DEFAULT_DISPLAY); + private static final MediaProjectionConfig USERS_CHOICE_CONFIG = + MediaProjectionConfig.createConfigForUserChoice(); + + @Before + public void setup() throws Exception { + mMockingSession = + mockitoSession() + .initMocks(this) + .strictness(LENIENT) + .startMocking(); + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + spyOn(mContext); + mMediaProjectionManager = new MediaProjectionManager(mContext); + } + + @After + public void teardown() { + mMockingSession.finishMocking(); + } + + @Test + public void testCreateScreenCaptureIntent() { + final String dialogPackage = "test.package"; + preparePermissionDialogComponent(dialogPackage); + + final Intent intent = mMediaProjectionManager.createScreenCaptureIntent(); + assertThat(intent).isNotNull(); + assertThat(intent.getComponent().getPackageName()).contains(dialogPackage); + } + + @Test + public void testCreateScreenCaptureIntent_display() { + final String dialogPackage = "test.package"; + preparePermissionDialogComponent(dialogPackage); + + final Intent intent = mMediaProjectionManager.createScreenCaptureIntent(DISPLAY_CONFIG); + assertThat(intent).isNotNull(); + assertThat(intent.getComponent().getPackageName()).contains(dialogPackage); + assertThat(intent.getParcelableExtra(EXTRA_MEDIA_PROJECTION_CONFIG, + MediaProjectionConfig.class)).isEqualTo(DISPLAY_CONFIG); + } + + @Test + public void testCreateScreenCaptureIntent_usersChoice() { + final String dialogPackage = "test.package"; + preparePermissionDialogComponent(dialogPackage); + + final Intent intent = mMediaProjectionManager.createScreenCaptureIntent( + USERS_CHOICE_CONFIG); + assertThat(intent).isNotNull(); + assertThat(intent.getComponent().getPackageName()).contains(dialogPackage); + assertThat(intent.getParcelableExtra(EXTRA_MEDIA_PROJECTION_CONFIG, + MediaProjectionConfig.class)).isEqualTo(USERS_CHOICE_CONFIG); + } + + private void preparePermissionDialogComponent(@NonNull String dialogPackage) { + final Resources mockResources = mock(Resources.class); + when(mContext.getResources()).thenReturn(mockResources); + doReturn(dialogPackage + "/.TestActivity").when(mockResources).getString( + com.android.internal.R.string + .config_mediaProjectionPermissionDialogComponent); + } +}