• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.wm.shell.common.split;
18 
19 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
20 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
21 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
22 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
23 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
24 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
25 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
26 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
27 
28 import android.content.Context;
29 import android.content.res.Configuration;
30 import android.graphics.PixelFormat;
31 import android.graphics.Rect;
32 import android.graphics.Region;
33 import android.os.Binder;
34 import android.view.IWindow;
35 import android.view.InsetsState;
36 import android.view.LayoutInflater;
37 import android.view.SurfaceControl;
38 import android.view.SurfaceControlViewHost;
39 import android.view.SurfaceSession;
40 import android.view.View;
41 import android.view.WindowManager;
42 import android.view.WindowlessWindowManager;
43 
44 import androidx.annotation.NonNull;
45 import androidx.annotation.Nullable;
46 
47 import com.android.wm.shell.R;
48 
49 /**
50  * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
51  */
52 public final class SplitWindowManager extends WindowlessWindowManager {
53     private static final String TAG = SplitWindowManager.class.getSimpleName();
54 
55     private final String mWindowName;
56     private final ParentContainerCallbacks mParentContainerCallbacks;
57     private Context mContext;
58     private SurfaceControlViewHost mViewHost;
59     private SurfaceControl mLeash;
60     private DividerView mDividerView;
61 
62     // Used to "pass" a transaction to WWM.remove so that view removal can be synchronized.
63     private SurfaceControl.Transaction mSyncTransaction = null;
64 
65     public interface ParentContainerCallbacks {
attachToParentSurface(SurfaceControl.Builder b)66         void attachToParentSurface(SurfaceControl.Builder b);
onLeashReady(SurfaceControl leash)67         void onLeashReady(SurfaceControl leash);
68     }
69 
SplitWindowManager(String windowName, Context context, Configuration config, ParentContainerCallbacks parentContainerCallbacks)70     public SplitWindowManager(String windowName, Context context, Configuration config,
71             ParentContainerCallbacks parentContainerCallbacks) {
72         super(config, null /* rootSurface */, null /* hostInputToken */);
73         mContext = context.createConfigurationContext(config);
74         mParentContainerCallbacks = parentContainerCallbacks;
75         mWindowName = windowName;
76     }
77 
setTouchRegion(@onNull Rect region)78     void setTouchRegion(@NonNull Rect region) {
79         if (mViewHost != null) {
80             setTouchRegion(mViewHost.getWindowToken().asBinder(), new Region(region));
81         }
82     }
83 
84     @Override
getSurfaceControl(IWindow window)85     public SurfaceControl getSurfaceControl(IWindow window) {
86         return super.getSurfaceControl(window);
87     }
88 
89     @Override
setConfiguration(Configuration configuration)90     public void setConfiguration(Configuration configuration) {
91         super.setConfiguration(configuration);
92         mContext = mContext.createConfigurationContext(configuration);
93     }
94 
95     @Override
attachToParentSurface(IWindow window, SurfaceControl.Builder b)96     protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
97         // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
98         final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
99                 .setContainerLayer()
100                 .setName(TAG)
101                 .setHidden(true)
102                 .setCallsite("SplitWindowManager#attachToParentSurface");
103         mParentContainerCallbacks.attachToParentSurface(builder);
104         mLeash = builder.build();
105         mParentContainerCallbacks.onLeashReady(mLeash);
106         b.setParent(mLeash);
107     }
108 
109     /** Inflates {@link DividerView} on to the root surface. */
init(SplitLayout splitLayout, InsetsState insetsState)110     void init(SplitLayout splitLayout, InsetsState insetsState) {
111         if (mDividerView != null || mViewHost != null) {
112             throw new UnsupportedOperationException(
113                     "Try to inflate divider view again without release first");
114         }
115 
116         mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
117         mDividerView = (DividerView) LayoutInflater.from(mContext)
118                 .inflate(R.layout.split_divider, null /* root */);
119 
120         final Rect dividerBounds = splitLayout.getDividerBounds();
121         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
122                 dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER,
123                 FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
124                         | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
125                 PixelFormat.TRANSLUCENT);
126         lp.token = new Binder();
127         lp.setTitle(mWindowName);
128         lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
129         mViewHost.setView(mDividerView, lp);
130         mDividerView.setup(splitLayout, this, mViewHost, insetsState);
131     }
132 
133     /**
134      * Releases the surface control of the current {@link DividerView} and tear down the view
135      * hierarchy.
136      */
release(@ullable SurfaceControl.Transaction t)137     void release(@Nullable SurfaceControl.Transaction t) {
138         if (mDividerView != null) {
139             mDividerView = null;
140         }
141 
142         if (mViewHost != null){
143             mSyncTransaction = t;
144             mViewHost.release();
145             mSyncTransaction = null;
146             mViewHost = null;
147         }
148 
149         if (mLeash != null) {
150             if (t == null) {
151                 new SurfaceControl.Transaction().remove(mLeash).apply();
152             } else {
153                 t.remove(mLeash);
154             }
155             mLeash = null;
156         }
157     }
158 
159     @Override
removeSurface(SurfaceControl sc)160     protected void removeSurface(SurfaceControl sc) {
161         // This gets called via SurfaceControlViewHost.release()
162         if (mSyncTransaction != null) {
163             mSyncTransaction.remove(sc);
164         } else {
165             super.removeSurface(sc);
166         }
167     }
168 
setInteractive(boolean interactive, String from)169     void setInteractive(boolean interactive, String from) {
170         if (mDividerView == null) return;
171         mDividerView.setInteractive(interactive, from);
172     }
173 
getDividerView()174     View getDividerView() {
175         return mDividerView;
176     }
177 
178     /**
179      * Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
180      * feasible.
181      */
182     @Nullable
getSurfaceControl()183     SurfaceControl getSurfaceControl() {
184         return mLeash;
185     }
186 
onInsetsChanged(InsetsState insetsState)187     void onInsetsChanged(InsetsState insetsState) {
188         if (mDividerView != null) {
189             mDividerView.onInsetsChanged(insetsState, true /* animate */);
190         }
191     }
192 }
193