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 package com.android.wm.shell.util; 17 18 import android.graphics.Rect; 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 22 import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; 23 24 import java.util.Objects; 25 26 /** 27 * Container of various information needed to display split screen 28 * tasks/leashes/etc in Launcher 29 */ 30 public class SplitBounds implements Parcelable { 31 public static final String KEY_EXTRA_SPLIT_BOUNDS = "key_SplitBounds"; 32 33 public final Rect leftTopBounds; 34 public final Rect rightBottomBounds; 35 /** This rect represents the actual gap between the two apps */ 36 public final Rect visualDividerBounds; 37 // This class is orientation-agnostic, so we compute both for later use 38 public final float topTaskPercent; 39 public final float leftTaskPercent; 40 public final float dividerWidthPercent; 41 public final float dividerHeightPercent; 42 public final @PersistentSnapPosition int snapPosition; 43 /** 44 * If {@code true}, that means at the time of creation of this object, the 45 * split-screened apps were vertically stacked. This is useful in scenarios like 46 * rotation where the bounds won't change, but this variable can indicate what orientation 47 * the bounds were originally in 48 */ 49 public final boolean appsStackedVertically; 50 public final int leftTopTaskId; 51 public final int rightBottomTaskId; 52 SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, int rightBottomTaskId, @PersistentSnapPosition int snapPosition)53 public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, 54 int rightBottomTaskId, @PersistentSnapPosition int snapPosition) { 55 this.leftTopBounds = leftTopBounds; 56 this.rightBottomBounds = rightBottomBounds; 57 this.leftTopTaskId = leftTopTaskId; 58 this.rightBottomTaskId = rightBottomTaskId; 59 this.snapPosition = snapPosition; 60 61 if (rightBottomBounds.top > leftTopBounds.top) { 62 // vertical apps, horizontal divider 63 this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom, 64 leftTopBounds.right, rightBottomBounds.top); 65 appsStackedVertically = true; 66 } else { 67 // horizontal apps, vertical divider 68 this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top, 69 rightBottomBounds.left, leftTopBounds.bottom); 70 appsStackedVertically = false; 71 } 72 73 float totalWidth = rightBottomBounds.right - leftTopBounds.left; 74 float totalHeight = rightBottomBounds.bottom - leftTopBounds.top; 75 leftTaskPercent = leftTopBounds.width() / totalWidth; 76 topTaskPercent = leftTopBounds.height() / totalHeight; 77 dividerWidthPercent = visualDividerBounds.width() / totalWidth; 78 dividerHeightPercent = visualDividerBounds.height() / totalHeight; 79 } 80 SplitBounds(Parcel parcel)81 public SplitBounds(Parcel parcel) { 82 leftTopBounds = parcel.readTypedObject(Rect.CREATOR); 83 rightBottomBounds = parcel.readTypedObject(Rect.CREATOR); 84 visualDividerBounds = parcel.readTypedObject(Rect.CREATOR); 85 topTaskPercent = parcel.readFloat(); 86 leftTaskPercent = parcel.readFloat(); 87 appsStackedVertically = parcel.readBoolean(); 88 leftTopTaskId = parcel.readInt(); 89 rightBottomTaskId = parcel.readInt(); 90 dividerWidthPercent = parcel.readFloat(); 91 dividerHeightPercent = parcel.readFloat(); 92 snapPosition = parcel.readInt(); 93 } 94 95 @Override writeToParcel(Parcel parcel, int flags)96 public void writeToParcel(Parcel parcel, int flags) { 97 parcel.writeTypedObject(leftTopBounds, flags); 98 parcel.writeTypedObject(rightBottomBounds, flags); 99 parcel.writeTypedObject(visualDividerBounds, flags); 100 parcel.writeFloat(topTaskPercent); 101 parcel.writeFloat(leftTaskPercent); 102 parcel.writeBoolean(appsStackedVertically); 103 parcel.writeInt(leftTopTaskId); 104 parcel.writeInt(rightBottomTaskId); 105 parcel.writeFloat(dividerWidthPercent); 106 parcel.writeFloat(dividerHeightPercent); 107 parcel.writeInt(snapPosition); 108 } 109 110 @Override describeContents()111 public int describeContents() { 112 return 0; 113 } 114 115 @Override equals(Object obj)116 public boolean equals(Object obj) { 117 if (!(obj instanceof SplitBounds)) { 118 return false; 119 } 120 // Only need to check the base fields (the other fields are derived from these) 121 final SplitBounds other = (SplitBounds) obj; 122 return Objects.equals(leftTopBounds, other.leftTopBounds) 123 && Objects.equals(rightBottomBounds, other.rightBottomBounds) 124 && leftTopTaskId == other.leftTopTaskId 125 && rightBottomTaskId == other.rightBottomTaskId; 126 } 127 128 @Override hashCode()129 public int hashCode() { 130 return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId); 131 } 132 133 @Override toString()134 public String toString() { 135 return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n" 136 + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n" 137 + "Divider: " + visualDividerBounds + "\n" 138 + "AppsVertical? " + appsStackedVertically + "\n" 139 + "snapPosition: " + snapPosition; 140 } 141 142 public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() { 143 @Override 144 public SplitBounds createFromParcel(Parcel in) { 145 return new SplitBounds(in); 146 } 147 148 @Override 149 public SplitBounds[] newArray(int size) { 150 return new SplitBounds[size]; 151 } 152 }; 153 } 154