1 /* 2 * Copyright (C) 2023 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 static android.app.TaskInfo.PROPERTY_VALUE_UNSET; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.graphics.Rect; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Objects; 31 32 /** 33 * Stores App Compat information about a particular Task. 34 * @hide 35 */ 36 public class AppCompatTaskInfo implements Parcelable { 37 /** 38 * If {@link #isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position 39 * or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise. 40 */ 41 public int topActivityLetterboxVerticalPosition = PROPERTY_VALUE_UNSET; 42 43 /** 44 * If {@link #isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position 45 * or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise. 46 */ 47 public int topActivityLetterboxHorizontalPosition = PROPERTY_VALUE_UNSET; 48 49 /** 50 * If {@link #isLetterboxDoubleTapEnabled} it contains the current width of the letterboxed 51 * activity or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise. 52 */ 53 public int topActivityLetterboxWidth = PROPERTY_VALUE_UNSET; 54 55 /** 56 * If {@link #isLetterboxDoubleTapEnabled} it contains the current height of the letterboxed 57 * activity or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise. 58 */ 59 public int topActivityLetterboxHeight = PROPERTY_VALUE_UNSET; 60 61 /** 62 * Contains the app bounds of the top activity or size compat mode 63 * bounds when in size compat mode. If null, contains bounds. 64 */ 65 @NonNull 66 public final Rect topActivityAppBounds = new Rect(); 67 68 /** 69 * Contains the top activity bounds when the activity is letterboxed. 70 * It's {@code null} if there's no top activity in the task or it's not letterboxed. 71 */ 72 // TODO(b/379824541) Remove duplicate information. 73 @Nullable 74 public Rect topActivityLetterboxBounds; 75 76 /** 77 * Stores camera-related app compat information about a particular Task. 78 */ 79 public CameraCompatTaskInfo cameraCompatTaskInfo = CameraCompatTaskInfo.create(); 80 81 /** Constant indicating no top activity flag has been set. */ 82 private static final int FLAG_UNDEFINED = 0x0; 83 /** Constant base value for top activity flag. */ 84 private static final int FLAG_BASE = 0x1; 85 /** Top activity flag for whether letterbox education is enabled. */ 86 private static final int FLAG_LETTERBOX_EDU_ENABLED = FLAG_BASE; 87 /** Top activity flag for whether activity is eligible for letterbox education. */ 88 private static final int FLAG_ELIGIBLE_FOR_LETTERBOX_EDU = FLAG_BASE << 1; 89 /** Top activity flag for whether activity bounds are letterboxed. */ 90 private static final int FLAG_LETTERBOXED = FLAG_BASE << 2; 91 /** Top activity flag for whether activity is in size compat mode. */ 92 private static final int FLAG_IN_SIZE_COMPAT = FLAG_BASE << 3; 93 /** Top activity flag for whether letterbox double tap is enabled. */ 94 private static final int FLAG_LETTERBOX_DOUBLE_TAP_ENABLED = FLAG_BASE << 4; 95 /** Top activity flag for whether the update comes from a letterbox double tap action. */ 96 private static final int FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP = FLAG_BASE << 5; 97 /** Top activity flag for whether activity is eligible for user aspect ratio button. */ 98 private static final int FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON = FLAG_BASE << 6; 99 /** Top activity flag for whether has activity has been overridden to fullscreen by system. */ 100 private static final int FLAG_FULLSCREEN_OVERRIDE_SYSTEM = FLAG_BASE << 7; 101 /** Top activity flag for whether has activity has been overridden to fullscreen by user. */ 102 private static final int FLAG_FULLSCREEN_OVERRIDE_USER = FLAG_BASE << 8; 103 /** Top activity flag for whether min aspect ratio of the activity has been overridden.*/ 104 public static final int FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE = FLAG_BASE << 9; 105 /** Top activity flag for whether restart menu is shown due to display move. */ 106 private static final int FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE = FLAG_BASE << 10; 107 /** Top activity flag for whether activity opted out of edge to edge. */ 108 public static final int FLAG_OPT_OUT_EDGE_TO_EDGE = FLAG_BASE << 11; 109 110 @Retention(RetentionPolicy.SOURCE) 111 @IntDef(flag = true, value = { 112 FLAG_UNDEFINED, 113 FLAG_LETTERBOX_EDU_ENABLED, 114 FLAG_ELIGIBLE_FOR_LETTERBOX_EDU, 115 FLAG_LETTERBOXED, 116 FLAG_IN_SIZE_COMPAT, 117 FLAG_LETTERBOX_DOUBLE_TAP_ENABLED, 118 FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP, 119 FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON, 120 FLAG_FULLSCREEN_OVERRIDE_SYSTEM, 121 FLAG_FULLSCREEN_OVERRIDE_USER, 122 FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, 123 FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE, 124 FLAG_OPT_OUT_EDGE_TO_EDGE 125 }) 126 public @interface TopActivityFlag {} 127 128 /** 129 * A combination of {@link TopActivityFlag}s that have been enabled through 130 * {@link #setTopActivityFlag}. 131 */ 132 @TopActivityFlag 133 private int mTopActivityFlags; 134 135 @TopActivityFlag 136 private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP 137 | FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM 138 | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE 139 | FLAG_OPT_OUT_EDGE_TO_EDGE | FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE; 140 141 @TopActivityFlag 142 private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED 143 | FLAG_IN_SIZE_COMPAT | FLAG_ELIGIBLE_FOR_LETTERBOX_EDU | FLAG_LETTERBOX_EDU_ENABLED 144 | FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE; 145 AppCompatTaskInfo()146 private AppCompatTaskInfo() { 147 // Do nothing 148 } 149 150 @NonNull create()151 static AppCompatTaskInfo create() { 152 return new AppCompatTaskInfo(); 153 } 154 AppCompatTaskInfo(Parcel source)155 private AppCompatTaskInfo(Parcel source) { 156 readFromParcel(source); 157 } 158 159 @Override describeContents()160 public int describeContents() { 161 return 0; 162 } 163 164 public static final Creator<AppCompatTaskInfo> CREATOR = 165 new Creator<>() { 166 @Override 167 public AppCompatTaskInfo createFromParcel(Parcel in) { 168 return new AppCompatTaskInfo(in); 169 } 170 171 @Override 172 public AppCompatTaskInfo[] newArray(int size) { 173 return new AppCompatTaskInfo[size]; 174 } 175 }; 176 177 /** 178 * @return {@code true} if the task has some compat ui. 179 */ hasCompatUI()180 public boolean hasCompatUI() { 181 return isTopActivityInSizeCompat() || eligibleForLetterboxEducation() 182 || isLetterboxDoubleTapEnabled() || eligibleForUserAspectRatioButton() 183 || isRestartMenuEnabledForDisplayMove(); 184 } 185 186 /** 187 * @return {@code true} if the top activity bounds are letterboxed with width <= height. 188 */ isTopActivityPillarboxShaped()189 public boolean isTopActivityPillarboxShaped() { 190 return isTopActivityLetterboxed() 191 && topActivityLetterboxWidth <= topActivityLetterboxHeight; 192 } 193 194 /** 195 * @return {@code true} if the letterbox education is enabled. 196 */ isLetterboxEducationEnabled()197 public boolean isLetterboxEducationEnabled() { 198 return isTopActivityFlagEnabled(FLAG_LETTERBOX_EDU_ENABLED); 199 } 200 201 /** 202 * Sets the top activity flag for whether letterbox education is enabled. 203 */ setLetterboxEducationEnabled(boolean enable)204 public void setLetterboxEducationEnabled(boolean enable) { 205 setTopActivityFlag(FLAG_LETTERBOX_EDU_ENABLED, enable); 206 } 207 208 /** 209 * @return {@code true} if the direct top activity is eligible for letterbox education. 210 */ eligibleForLetterboxEducation()211 public boolean eligibleForLetterboxEducation() { 212 return isTopActivityFlagEnabled(FLAG_ELIGIBLE_FOR_LETTERBOX_EDU); 213 } 214 215 /** 216 * Sets the top activity flag to be eligible for letterbox education. 217 */ setEligibleForLetterboxEducation(boolean enable)218 public void setEligibleForLetterboxEducation(boolean enable) { 219 setTopActivityFlag(FLAG_ELIGIBLE_FOR_LETTERBOX_EDU, enable); 220 } 221 222 /** 223 * @return {@code true} if the direct top activity is eligible for the user aspect ratio 224 * settings button. 225 */ eligibleForUserAspectRatioButton()226 public boolean eligibleForUserAspectRatioButton() { 227 return isTopActivityFlagEnabled(FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON); 228 } 229 230 /** 231 * Sets the top activity flag to be eligible for the user aspect ratio settings button. 232 */ setEligibleForUserAspectRatioButton(boolean enable)233 public void setEligibleForUserAspectRatioButton(boolean enable) { 234 setTopActivityFlag(FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON, enable); 235 } 236 237 /** 238 * @return {@code true} if double tap to reposition letterboxed app is enabled. 239 */ isLetterboxDoubleTapEnabled()240 public boolean isLetterboxDoubleTapEnabled() { 241 return isTopActivityFlagEnabled(FLAG_LETTERBOX_DOUBLE_TAP_ENABLED); 242 } 243 244 /** 245 * Sets the top activity flag to enable double tap to reposition letterboxed app. 246 */ setLetterboxDoubleTapEnabled(boolean enable)247 public void setLetterboxDoubleTapEnabled(boolean enable) { 248 setTopActivityFlag(FLAG_LETTERBOX_DOUBLE_TAP_ENABLED, enable); 249 } 250 251 /** 252 * @return {@code true} if the update comes from a letterbox double-tap action from the user. 253 */ isFromLetterboxDoubleTap()254 public boolean isFromLetterboxDoubleTap() { 255 return isTopActivityFlagEnabled(FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP); 256 } 257 258 /** 259 * Sets the top activity flag for whether the update comes from a letterbox double-tap action 260 * from the user. 261 */ setIsFromLetterboxDoubleTap(boolean enable)262 public void setIsFromLetterboxDoubleTap(boolean enable) { 263 setTopActivityFlag(FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP, enable); 264 } 265 266 /** 267 * @return {@code true} if the user has forced the activity to be fullscreen through the 268 * user aspect ratio settings. 269 */ isUserFullscreenOverrideEnabled()270 public boolean isUserFullscreenOverrideEnabled() { 271 return isTopActivityFlagEnabled(FLAG_FULLSCREEN_OVERRIDE_USER); 272 } 273 274 /** 275 * Sets the top activity flag for whether the user has forced the activity to be fullscreen 276 * through the user aspect ratio settings. 277 */ setUserFullscreenOverrideEnabled(boolean enable)278 public void setUserFullscreenOverrideEnabled(boolean enable) { 279 setTopActivityFlag(FLAG_FULLSCREEN_OVERRIDE_USER, enable); 280 } 281 282 /** 283 * @return {@code true} if the system has forced the activity to be fullscreen. 284 */ isSystemFullscreenOverrideEnabled()285 public boolean isSystemFullscreenOverrideEnabled() { 286 return isTopActivityFlagEnabled(FLAG_FULLSCREEN_OVERRIDE_SYSTEM); 287 } 288 289 /** 290 * Sets the top activity flag for whether the system has forced the activity to be fullscreen. 291 */ setSystemFullscreenOverrideEnabled(boolean enable)292 public void setSystemFullscreenOverrideEnabled(boolean enable) { 293 setTopActivityFlag(FLAG_FULLSCREEN_OVERRIDE_SYSTEM, enable); 294 } 295 296 /** 297 * @return {@code true} if the direct top activity is in size compat mode on foreground. 298 */ isTopActivityInSizeCompat()299 public boolean isTopActivityInSizeCompat() { 300 return isTopActivityFlagEnabled(FLAG_IN_SIZE_COMPAT); 301 } 302 303 /** 304 * Sets the top activity flag for whether the direct top activity is in size compat mode 305 * on foreground. 306 */ setTopActivityInSizeCompat(boolean enable)307 public void setTopActivityInSizeCompat(boolean enable) { 308 setTopActivityFlag(FLAG_IN_SIZE_COMPAT, enable); 309 } 310 311 /** 312 * @return {@code true} if the restart menu is enabled for the top activity due to display move. 313 */ isRestartMenuEnabledForDisplayMove()314 public boolean isRestartMenuEnabledForDisplayMove() { 315 return isTopActivityFlagEnabled(FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE); 316 } 317 318 /** 319 * Sets the top activity flag for whether the restart menu is enabled for the top activity due 320 * to display move. 321 */ setRestartMenuEnabledForDisplayMove(boolean enable)322 public void setRestartMenuEnabledForDisplayMove(boolean enable) { 323 setTopActivityFlag(FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE, enable); 324 } 325 326 /** 327 * @return {@code true} if the top activity bounds are letterboxed. 328 */ isTopActivityLetterboxed()329 public boolean isTopActivityLetterboxed() { 330 return isTopActivityFlagEnabled(FLAG_LETTERBOXED); 331 } 332 333 /** 334 * Sets the top activity flag for whether the top activity bounds are letterboxed. 335 */ setTopActivityLetterboxed(boolean enable)336 public void setTopActivityLetterboxed(boolean enable) { 337 setTopActivityFlag(FLAG_LETTERBOXED, enable); 338 } 339 340 /** 341 * @return {@code true} if the top activity's min aspect ratio has been overridden. 342 */ hasMinAspectRatioOverride()343 public boolean hasMinAspectRatioOverride() { 344 return isTopActivityFlagEnabled(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE); 345 } 346 347 /** 348 * Sets the top activity flag for whether the min aspect ratio of the activity has been 349 * overridden. 350 */ setHasMinAspectRatioOverride(boolean enable)351 public void setHasMinAspectRatioOverride(boolean enable) { 352 setTopActivityFlag(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, enable); 353 } 354 355 /** 356 * Sets the top activity flag for whether the activity has opted out of edge to edge. 357 */ setOptOutEdgeToEdge(boolean enable)358 public void setOptOutEdgeToEdge(boolean enable) { 359 setTopActivityFlag(FLAG_OPT_OUT_EDGE_TO_EDGE, enable); 360 } 361 362 /** 363 * @return {@code true} if the top activity has opted out of edge to edge. 364 */ hasOptOutEdgeToEdge()365 public boolean hasOptOutEdgeToEdge() { 366 return isTopActivityFlagEnabled(FLAG_OPT_OUT_EDGE_TO_EDGE); 367 } 368 369 /** Clear all top activity flags and set to false. */ clearTopActivityFlags()370 public void clearTopActivityFlags() { 371 mTopActivityFlags = FLAG_UNDEFINED; 372 } 373 374 /** 375 * @return {@code true} if the app compat parameters that are important for task organizers 376 * are equal. 377 */ equalsForTaskOrganizer(@ullable AppCompatTaskInfo that)378 public boolean equalsForTaskOrganizer(@Nullable AppCompatTaskInfo that) { 379 if (that == null) { 380 return false; 381 } 382 return (mTopActivityFlags & FLAGS_ORGANIZER_INTERESTED) 383 == (that.mTopActivityFlags & FLAGS_ORGANIZER_INTERESTED) 384 && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition 385 && topActivityLetterboxWidth == that.topActivityLetterboxWidth 386 && topActivityLetterboxHeight == that.topActivityLetterboxHeight 387 && topActivityAppBounds.equals(that.topActivityAppBounds) 388 && topActivityLetterboxHorizontalPosition 389 == that.topActivityLetterboxHorizontalPosition 390 && cameraCompatTaskInfo.equalsForTaskOrganizer(that.cameraCompatTaskInfo); 391 } 392 393 /** 394 * @return {@code true} if parameters that are important for size compat have changed. 395 */ equalsForCompatUi(@ullable AppCompatTaskInfo that)396 public boolean equalsForCompatUi(@Nullable AppCompatTaskInfo that) { 397 if (that == null) { 398 return false; 399 } 400 return (mTopActivityFlags & FLAGS_COMPAT_UI_INTERESTED) 401 == (that.mTopActivityFlags & FLAGS_COMPAT_UI_INTERESTED) 402 && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition 403 && topActivityLetterboxHorizontalPosition 404 == that.topActivityLetterboxHorizontalPosition 405 && topActivityLetterboxWidth == that.topActivityLetterboxWidth 406 && topActivityLetterboxHeight == that.topActivityLetterboxHeight 407 && topActivityAppBounds.equals(that.topActivityAppBounds) 408 && cameraCompatTaskInfo.equalsForCompatUi(that.cameraCompatTaskInfo); 409 } 410 411 /** 412 * Reads the AppCompatTaskInfo from a parcel. 413 */ readFromParcel(Parcel source)414 void readFromParcel(Parcel source) { 415 mTopActivityFlags = source.readInt(); 416 topActivityLetterboxVerticalPosition = source.readInt(); 417 topActivityLetterboxHorizontalPosition = source.readInt(); 418 topActivityLetterboxWidth = source.readInt(); 419 topActivityLetterboxHeight = source.readInt(); 420 topActivityAppBounds.set(Objects.requireNonNull(source.readTypedObject(Rect.CREATOR))); 421 topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR); 422 cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR); 423 } 424 425 /** 426 * Writes the AppCompatTaskInfo to a parcel. 427 */ 428 @Override writeToParcel(Parcel dest, int flags)429 public void writeToParcel(Parcel dest, int flags) { 430 dest.writeInt(mTopActivityFlags); 431 dest.writeInt(topActivityLetterboxVerticalPosition); 432 dest.writeInt(topActivityLetterboxHorizontalPosition); 433 dest.writeInt(topActivityLetterboxWidth); 434 dest.writeInt(topActivityLetterboxHeight); 435 dest.writeTypedObject(topActivityAppBounds, flags); 436 dest.writeTypedObject(topActivityLetterboxBounds, flags); 437 dest.writeTypedObject(cameraCompatTaskInfo, flags); 438 } 439 440 @Override toString()441 public String toString() { 442 return "AppCompatTaskInfo { topActivityInSizeCompat=" + isTopActivityInSizeCompat() 443 + " eligibleForLetterboxEducation= " + eligibleForLetterboxEducation() 444 + " isLetterboxEducationEnabled= " + isLetterboxEducationEnabled() 445 + " isLetterboxDoubleTapEnabled= " + isLetterboxDoubleTapEnabled() 446 + " eligibleForUserAspectRatioButton= " + eligibleForUserAspectRatioButton() 447 + " topActivityBoundsLetterboxed= " + isTopActivityLetterboxed() 448 + " isFromLetterboxDoubleTap= " + isFromLetterboxDoubleTap() 449 + " topActivityLetterboxVerticalPosition= " + topActivityLetterboxVerticalPosition 450 + " topActivityLetterboxHorizontalPosition= " 451 + topActivityLetterboxHorizontalPosition 452 + " topActivityLetterboxWidth=" + topActivityLetterboxWidth 453 + " topActivityLetterboxHeight=" + topActivityLetterboxHeight 454 + " topActivityAppBounds=" + topActivityAppBounds 455 + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled() 456 + " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled() 457 + " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride() 458 + " topActivityLetterboxBounds=" + topActivityLetterboxBounds 459 + " cameraCompatTaskInfo=" + cameraCompatTaskInfo.toString() 460 + "}"; 461 } 462 setTopActivityFlag(@opActivityFlag int flag, boolean enable)463 private void setTopActivityFlag(@TopActivityFlag int flag, boolean enable) { 464 mTopActivityFlags = enable ? (mTopActivityFlags | flag) : (mTopActivityFlags & ~flag); 465 } 466 isTopActivityFlagEnabled(@opActivityFlag int flag)467 private boolean isTopActivityFlagEnabled(@TopActivityFlag int flag) { 468 return (mTopActivityFlags & flag) == flag; 469 } 470 } 471