1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.TestApi;
22 import android.content.res.Configuration;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.util.Objects;
27 
28 /**
29  * Used by {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)}.
30  */
31 public final class PictureInPictureUiState implements Parcelable {
32 
33     private final boolean mIsStashed;
34     private final boolean mIsTransitioningToPip;
35 
36     /** {@hide} */
PictureInPictureUiState(Parcel in)37     PictureInPictureUiState(Parcel in) {
38         mIsStashed = in.readBoolean();
39         mIsTransitioningToPip = in.readBoolean();
40     }
41 
42     /** {@hide} */
43     @TestApi
PictureInPictureUiState(boolean isStashed)44     public PictureInPictureUiState(boolean isStashed) {
45         this(isStashed, false /* isEnteringPip */);
46     }
47 
PictureInPictureUiState(boolean isStashed, boolean isTransitioningToPip)48     private PictureInPictureUiState(boolean isStashed, boolean isTransitioningToPip) {
49         mIsStashed = isStashed;
50         mIsTransitioningToPip = isTransitioningToPip;
51     }
52 
53     /**
54      * Returns whether Picture-in-Picture is stashed or not. A stashed PiP means it is only
55      * partially visible to the user, with some parts of it being off-screen. This is usually a
56      * UI state that is triggered by the user, such as flinging the PiP to the edge or letting go
57      * of PiP while dragging partially off-screen.
58      *
59      * Developers can use this in conjunction with
60      * {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} to get a signal
61      * when the PiP stash state has changed. For example, if the state changed from {@code false} to
62      * {@code true}, developers can choose to temporarily pause video playback if PiP is of video
63      * content. Vice versa, if changing from {@code true} to {@code false} and video content is
64      * paused, developers can resume video playback.
65      *
66      * @see <a href="http://developer.android.com/about/versions/12/features/pip-improvements">
67      *     Picture in Picture (PiP) improvements</a>
68      */
isStashed()69     public boolean isStashed() {
70         return mIsStashed;
71     }
72 
73     /**
74      * Returns {@code true} if the app is going to enter Picture-in-Picture (PiP) mode.
75      *
76      * This state is associated with the entering PiP animation. When that animation starts,
77      * whether via auto enter PiP or calling
78      * {@link Activity#enterPictureInPictureMode(PictureInPictureParams)} explicitly, app can expect
79      * {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
80      * {@link #isTransitioningToPip()} to be {@code true} first,
81      * followed by {@link Activity#onPictureInPictureModeChanged(boolean, Configuration)} when it
82      * fully settles in PiP mode.
83      *
84      * When app receives the
85      * {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
86      * {@link #isTransitioningToPip()} being {@code true}, it's recommended to hide certain UI
87      * elements, such as video controls, to archive a clean entering PiP animation.
88      *
89      * In case an application wants to restore the previously hidden UI elements when exiting
90      * PiP, it is recommended to do that in
91      * {@code onPictureInPictureModeChanged(isInPictureInPictureMode=false)} callback rather
92      * than the beginning of exit PiP animation.
93      */
94     @FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
isTransitioningToPip()95     public boolean isTransitioningToPip() {
96         return mIsTransitioningToPip;
97     }
98 
99     @Override
equals(Object o)100     public boolean equals(Object o) {
101         if (this == o) return true;
102         if (!(o instanceof PictureInPictureUiState)) return false;
103         PictureInPictureUiState that = (PictureInPictureUiState) o;
104         return mIsStashed == that.mIsStashed
105                 && mIsTransitioningToPip == that.mIsTransitioningToPip;
106     }
107 
108     @Override
hashCode()109     public int hashCode() {
110         return Objects.hash(mIsStashed, mIsTransitioningToPip);
111     }
112 
113     @Override
describeContents()114     public int describeContents() {
115         return 0;
116     }
117 
118     @Override
writeToParcel(@onNull Parcel out, int flags)119     public void writeToParcel(@NonNull Parcel out, int flags) {
120         out.writeBoolean(mIsStashed);
121         out.writeBoolean(mIsTransitioningToPip);
122     }
123 
124     public static final @android.annotation.NonNull Creator<PictureInPictureUiState> CREATOR =
125             new Creator<PictureInPictureUiState>() {
126                 public PictureInPictureUiState createFromParcel(Parcel in) {
127                     return new PictureInPictureUiState(in);
128                 }
129                 public PictureInPictureUiState[] newArray(int size) {
130                     return new PictureInPictureUiState[size];
131                 }
132             };
133 
134     /**
135      * Builder class for {@link PictureInPictureUiState}.
136      * @hide
137      */
138     @FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
139     public static final class Builder {
140         private boolean mIsStashed;
141         private boolean mIsTransitioningToPip;
142 
143         /** Empty constructor. */
Builder()144         public Builder() {
145         }
146 
147         /**
148          * Sets the {@link #mIsStashed} state.
149          * @return The same {@link Builder} instance.
150          */
setStashed(boolean isStashed)151         public Builder setStashed(boolean isStashed) {
152             mIsStashed = isStashed;
153             return this;
154         }
155 
156         /**
157          * Sets the {@link #mIsTransitioningToPip} state.
158          * @return The same {@link Builder} instance.
159          */
setTransitioningToPip(boolean isEnteringPip)160         public Builder setTransitioningToPip(boolean isEnteringPip) {
161             mIsTransitioningToPip = isEnteringPip;
162             return this;
163         }
164 
165         /**
166          * @return The constructed {@link PictureInPictureUiState} instance.
167          */
build()168         public PictureInPictureUiState build() {
169             return new PictureInPictureUiState(mIsStashed, mIsTransitioningToPip);
170         }
171     }
172 }
173