Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a97ff8e4 authored by Vadim Caen's avatar Vadim Caen
Browse files

Introduce MediaProjectionAppContent

Test: MediaProjectionAppContentTest
Flag: com.android.media.projection.flags.app_content_sharing
Bug: 398757866
Change-Id: I5dc3a487c5aa9d1e5df443a2d0dec2fcf3556c5e
parent 73b14152
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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<android.media.projection.MediaProjectionAppContent> 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<android.media.projection.MediaProjectionConfig> 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
  }
+19 −0
Original line number Diff line number Diff line
/*
 * 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
+123 −0
Original line number Diff line number Diff line
/*
 * 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.
 * <p>
 * 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.
 * <p>
 * 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<MediaProjectionAppContent> 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];
                }
            };
}
+11 −3
Original line number Diff line number Diff line
@@ -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 {
    }
+86 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}