1 /*
2  * Copyright (C) 2020 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.quickstep.util;
17 
18 import static android.view.Surface.ROTATION_0;
19 import static android.view.Surface.ROTATION_180;
20 
21 import android.annotation.TargetApi;
22 import android.content.Context;
23 import android.graphics.Insets;
24 import android.graphics.Rect;
25 import android.os.Build;
26 import android.view.WindowInsets.Type;
27 import android.view.WindowManager;
28 import android.view.WindowMetrics;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.Nullable;
32 import androidx.annotation.UiThread;
33 
34 import com.android.launcher3.R;
35 import com.android.launcher3.util.DefaultDisplay;
36 import com.android.launcher3.util.WindowBounds;
37 
38 import java.util.ArrayList;
39 
40 /**
41  * Utility class to hold the information abound a window bounds for split screen
42  */
43 @TargetApi(Build.VERSION_CODES.R)
44 public class SplitScreenBounds {
45 
46     public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
47     private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
48 
49     @Nullable
50     private WindowBounds mBounds;
51 
SplitScreenBounds()52     private SplitScreenBounds() { }
53 
54     @UiThread
setSecondaryWindowBounds(@onNull WindowBounds bounds)55     public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
56         if (!bounds.equals(mBounds)) {
57             mBounds = bounds;
58             for (OnChangeListener listener : mListeners) {
59                 listener.onSecondaryWindowBoundsChanged();
60             }
61         }
62     }
63 
getSecondaryWindowBounds(Context context)64     public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
65         if (mBounds == null) {
66             mBounds = createDefaultWindowBounds(context);
67         }
68         return mBounds;
69     }
70 
71     /**
72      * Creates window bounds as 50% of device size
73      */
createDefaultWindowBounds(Context context)74     private static WindowBounds createDefaultWindowBounds(Context context) {
75         WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
76         Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
77 
78         WindowBounds bounds = new WindowBounds(wm.getBounds(),
79                 new Rect(insets.left, insets.top, insets.right, insets.bottom));
80         int rotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
81         int halfDividerSize = context.getResources()
82                 .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
83 
84         if (rotation == ROTATION_0 || rotation == ROTATION_180) {
85             bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
86             bounds.insets.top = 0;
87         } else {
88             bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
89             bounds.insets.left = 0;
90         }
91         return new WindowBounds(bounds.bounds, bounds.insets);
92     }
93 
addOnChangeListener(OnChangeListener listener)94     public void addOnChangeListener(OnChangeListener listener) {
95         mListeners.add(listener);
96     }
97 
removeOnChangeListener(OnChangeListener listener)98     public void removeOnChangeListener(OnChangeListener listener) {
99         mListeners.remove(listener);
100     }
101 
102     /**
103      * Interface to receive window bounds changes
104      */
105     public interface OnChangeListener {
106 
107         /**
108          * Called when window bounds for secondary window changes
109          */
onSecondaryWindowBoundsChanged()110         void onSecondaryWindowBoundsChanged();
111     }
112 }
113