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