From a97ff8e4a2102603d8c41a5989e8eb91e69a4fc1 Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Thu, 27 Feb 2025 15:07:55 +0100 Subject: Introduce MediaProjectionAppContent Test: MediaProjectionAppContentTest Flag: com.android.media.projection.flags.app_content_sharing Bug: 398757866 Change-Id: I5dc3a487c5aa9d1e5df443a2d0dec2fcf3556c5e --- core/api/current.txt | 8 ++ .../projection/MediaProjectionAppContent.aidl | 19 ++++ .../projection/MediaProjectionAppContent.java | 123 +++++++++++++++++++++ .../media/projection/MediaProjectionConfig.java | 14 ++- .../projection/MediaProjectionAppContentTest.java | 86 ++++++++++++++ 5 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 media/java/android/media/projection/MediaProjectionAppContent.aidl create mode 100644 media/java/android/media/projection/MediaProjectionAppContent.java create mode 100644 media/tests/projection/src/android/media/projection/MediaProjectionAppContentTest.java diff --git a/core/api/current.txt b/core/api/current.txt index c666328907ae..55e14b124885 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -27223,6 +27223,13 @@ package android.media.projection { method public void onStop(); } + @FlaggedApi("com.android.media.projection.flags.app_content_sharing") public final class MediaProjectionAppContent implements android.os.Parcelable { + ctor public MediaProjectionAppContent(@NonNull android.graphics.Bitmap, @NonNull CharSequence, int); + 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 MediaProjectionConfig implements android.os.Parcelable { method @NonNull public static android.media.projection.MediaProjectionConfig createConfigForDefaultDisplay(); method @NonNull public static android.media.projection.MediaProjectionConfig createConfigForUserChoice(); @@ -27234,6 +27241,7 @@ package android.media.projection { method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; field @FlaggedApi("com.android.media.projection.flags.app_content_sharing") public static final int PROJECTION_SOURCE_APP = 8; // 0x8 + field @FlaggedApi("com.android.media.projection.flags.app_content_sharing") public static final int PROJECTION_SOURCE_APP_CONTENT = 16; // 0x10 field @FlaggedApi("com.android.media.projection.flags.app_content_sharing") public static final int PROJECTION_SOURCE_DISPLAY = 2; // 0x2 } diff --git a/media/java/android/media/projection/MediaProjectionAppContent.aidl b/media/java/android/media/projection/MediaProjectionAppContent.aidl new file mode 100644 index 000000000000..6ead69b9fdc6 --- /dev/null +++ b/media/java/android/media/projection/MediaProjectionAppContent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2025 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 MediaProjectionAppContent; \ No newline at end of file diff --git a/media/java/android/media/projection/MediaProjectionAppContent.java b/media/java/android/media/projection/MediaProjectionAppContent.java new file mode 100644 index 000000000000..da0bdc191c0c --- /dev/null +++ b/media/java/android/media/projection/MediaProjectionAppContent.java @@ -0,0 +1,123 @@ +/* + * Copyright 2025 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 android.annotation.FlaggedApi; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.Objects; + +/** + * Holds information about content an app can share via the MediaProjection APIs. + *

+ * An application requesting a {@link MediaProjection session} can add its own content in the + * list of available content along with the whole screen or a single application. + *

+ * Each instance of {@link MediaProjectionAppContent} contains an id that is used to identify the + * content chosen by the user back to the advertising application, thus the meaning of the id is + * only relevant to that application. + */ +@FlaggedApi(com.android.media.projection.flags.Flags.FLAG_APP_CONTENT_SHARING) +public final class MediaProjectionAppContent implements Parcelable { + + private final Bitmap mThumbnail; + private final CharSequence mTitle; + private final int mId; + + /** + * Constructor to pass a thumbnail, title and id. + * + * @param thumbnail The thumbnail representing this content to be shown to the user. + * @param title A user visible string representing the title of this content. + * @param id An arbitrary int defined by the advertising application to be fed back once + * the user made their choice. + */ + public MediaProjectionAppContent(@NonNull Bitmap thumbnail, @NonNull CharSequence title, + int id) { + mThumbnail = Objects.requireNonNull(thumbnail, "thumbnail can't be null").asShared(); + mTitle = Objects.requireNonNull(title, "title can't be null"); + mId = id; + } + + /** + * Returns thumbnail representing this content to be shown to the user. + * + * @hide + */ + @NonNull + public Bitmap getThumbnail() { + return mThumbnail; + } + + /** + * Returns user visible string representing the title of this content. + * + * @hide + */ + @NonNull + public CharSequence getTitle() { + return mTitle; + } + + /** + * Returns the arbitrary int defined by the advertising application to be fed back once + * the user made their choice. + * + * @hide + */ + public int getId() { + return mId; + } + + private MediaProjectionAppContent(Parcel in) { + mThumbnail = in.readParcelable(this.getClass().getClassLoader(), Bitmap.class); + mTitle = in.readCharSequence(); + mId = in.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mThumbnail, flags); + dest.writeCharSequence(mTitle); + dest.writeInt(mId); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator CREATOR = + new Creator<>() { + @NonNull + @Override + public MediaProjectionAppContent createFromParcel(@NonNull Parcel in) { + return new MediaProjectionAppContent(in); + } + + @NonNull + @Override + public MediaProjectionAppContent[] newArray(int size) { + return new MediaProjectionAppContent[size]; + } + }; +} diff --git a/media/java/android/media/projection/MediaProjectionConfig.java b/media/java/android/media/projection/MediaProjectionConfig.java index 1b63c795bf90..cd674e9f2ad1 100644 --- a/media/java/android/media/projection/MediaProjectionConfig.java +++ b/media/java/android/media/projection/MediaProjectionConfig.java @@ -62,6 +62,13 @@ public final class MediaProjectionConfig implements Parcelable { @FlaggedApi(Flags.FLAG_APP_CONTENT_SHARING) public static final int PROJECTION_SOURCE_APP = 1 << 3; + /** + * Bitmask for setting whether this configuration is for projecting the content provided by an + * application. + */ + @FlaggedApi(com.android.media.projection.flags.Flags.FLAG_APP_CONTENT_SHARING) + public static final int PROJECTION_SOURCE_APP_CONTENT = 1 << 4; + /** * The user, rather than the host app, determines which region of the display to capture. * @@ -84,11 +91,12 @@ public final class MediaProjectionConfig implements Parcelable { private static final int[] PROJECTION_SOURCES = new int[]{PROJECTION_SOURCE_DISPLAY, PROJECTION_SOURCE_DISPLAY_REGION, - PROJECTION_SOURCE_APP}; + PROJECTION_SOURCE_APP, + PROJECTION_SOURCE_APP_CONTENT}; private static final String[] PROJECTION_SOURCES_STRING = new String[]{"PROJECTION_SOURCE_DISPLAY", "PROJECTION_SOURCE_DISPLAY_REGION", - "PROJECTION_SOURCE_APP"}; + "PROJECTION_SOURCE_APP", "PROJECTION_SOURCE_APP_CONTENT"}; private static final int VALID_PROJECTION_SOURCES = createValidSourcesMask(); @@ -104,7 +112,7 @@ public final class MediaProjectionConfig implements Parcelable { /** @hide */ @IntDef(flag = true, prefix = "PROJECTION_SOURCE_", value = {PROJECTION_SOURCE_DISPLAY, - PROJECTION_SOURCE_DISPLAY_REGION, PROJECTION_SOURCE_APP}) + PROJECTION_SOURCE_DISPLAY_REGION, PROJECTION_SOURCE_APP, PROJECTION_SOURCE_APP_CONTENT}) @Retention(SOURCE) public @interface MediaProjectionSource { } diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionAppContentTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionAppContentTest.java new file mode 100644 index 000000000000..7e167c63a2a2 --- /dev/null +++ b/media/tests/projection/src/android/media/projection/MediaProjectionAppContentTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2025 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 com.google.common.truth.Truth.assertThat; + +import android.graphics.Bitmap; +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class MediaProjectionAppContentTest { + + @Test + public void testConstructorAndGetters() { + // Create a mock Bitmap + Bitmap mockBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + + // Create a MediaProjectionAppContent object + MediaProjectionAppContent content = new MediaProjectionAppContent(mockBitmap, "Test Title", + 123); + + // Verify the values using getters + assertThat(content.getTitle()).isEqualTo("Test Title"); + assertThat(content.getId()).isEqualTo(123); + // Compare bitmap configurations and dimensions + assertThat(content.getThumbnail().getConfig()).isEqualTo(mockBitmap.getConfig()); + assertThat(content.getThumbnail().getWidth()).isEqualTo(mockBitmap.getWidth()); + assertThat(content.getThumbnail().getHeight()).isEqualTo(mockBitmap.getHeight()); + } + + @Test + public void testParcelable() { + // Create a mock Bitmap + Bitmap mockBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + + // Create a MediaProjectionAppContent object + MediaProjectionAppContent content = new MediaProjectionAppContent(mockBitmap, "Test Title", + 123); + + // Parcel and unparcel the object + Parcel parcel = Parcel.obtain(); + content.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + MediaProjectionAppContent unparceledContent = + MediaProjectionAppContent.CREATOR.createFromParcel(parcel); + + // Verify the values of the unparceled object + assertThat(unparceledContent.getTitle()).isEqualTo("Test Title"); + assertThat(unparceledContent.getId()).isEqualTo(123); + // Compare bitmap configurations and dimensions + assertThat(unparceledContent.getThumbnail().getConfig()).isEqualTo(mockBitmap.getConfig()); + assertThat(unparceledContent.getThumbnail().getWidth()).isEqualTo(mockBitmap.getWidth()); + assertThat(unparceledContent.getThumbnail().getHeight()).isEqualTo(mockBitmap.getHeight()); + + parcel.recycle(); + } + + @Test + public void testCreatorNewArray() { + // Create a new array using the CREATOR + MediaProjectionAppContent[] contentArray = MediaProjectionAppContent.CREATOR.newArray(5); + + // Verify that the array is not null and has the correct size + assertThat(contentArray).isNotNull(); + assertThat(contentArray).hasLength(5); + } +} -- cgit v1.2.3-59-g8ed1b