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