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.WindowManager; 41 import android.view.WindowlessWindowManager; 42 43 import androidx.annotation.NonNull; 44 import androidx.annotation.Nullable; 45 46 import com.android.wm.shell.R; 47 48 /** 49 * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split. 50 */ 51 public final class SplitWindowManager extends WindowlessWindowManager { 52 private static final String TAG = SplitWindowManager.class.getSimpleName(); 53 54 private final String mWindowName; 55 private final ParentContainerCallbacks mParentContainerCallbacks; 56 private Context mContext; 57 private SurfaceControlViewHost mViewHost; 58 private SurfaceControl mLeash; 59 private DividerView mDividerView; 60 61 public interface ParentContainerCallbacks { attachToParentSurface(SurfaceControl.Builder b)62 void attachToParentSurface(SurfaceControl.Builder b); onLeashReady(SurfaceControl leash)63 void onLeashReady(SurfaceControl leash); 64 } 65 SplitWindowManager(String windowName, Context context, Configuration config, ParentContainerCallbacks parentContainerCallbacks)66 public SplitWindowManager(String windowName, Context context, Configuration config, 67 ParentContainerCallbacks parentContainerCallbacks) { 68 super(config, null /* rootSurface */, null /* hostInputToken */); 69 mContext = context.createConfigurationContext(config); 70 mParentContainerCallbacks = parentContainerCallbacks; 71 mWindowName = windowName; 72 } 73 setTouchRegion(@onNull Rect region)74 void setTouchRegion(@NonNull Rect region) { 75 if (mViewHost != null) { 76 setTouchRegion(mViewHost.getWindowToken().asBinder(), new Region(region)); 77 } 78 } 79 80 @Override getSurfaceControl(IWindow window)81 public SurfaceControl getSurfaceControl(IWindow window) { 82 return super.getSurfaceControl(window); 83 } 84 85 @Override setConfiguration(Configuration configuration)86 public void setConfiguration(Configuration configuration) { 87 super.setConfiguration(configuration); 88 mContext = mContext.createConfigurationContext(configuration); 89 } 90 91 @Override attachToParentSurface(IWindow window, SurfaceControl.Builder b)92 protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { 93 // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. 94 final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) 95 .setContainerLayer() 96 .setName(TAG) 97 .setHidden(true) 98 .setCallsite("SplitWindowManager#attachToParentSurface"); 99 mParentContainerCallbacks.attachToParentSurface(builder); 100 mLeash = builder.build(); 101 mParentContainerCallbacks.onLeashReady(mLeash); 102 b.setParent(mLeash); 103 } 104 105 /** Inflates {@link DividerView} on to the root surface. */ init(SplitLayout splitLayout, InsetsState insetsState)106 void init(SplitLayout splitLayout, InsetsState insetsState) { 107 if (mDividerView != null || mViewHost != null) { 108 throw new UnsupportedOperationException( 109 "Try to inflate divider view again without release first"); 110 } 111 112 mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); 113 mDividerView = (DividerView) LayoutInflater.from(mContext) 114 .inflate(R.layout.split_divider, null /* root */); 115 116 final Rect dividerBounds = splitLayout.getDividerBounds(); 117 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 118 dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER, 119 FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH 120 | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, 121 PixelFormat.TRANSLUCENT); 122 lp.token = new Binder(); 123 lp.setTitle(mWindowName); 124 lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; 125 mViewHost.setView(mDividerView, lp); 126 mDividerView.setup(splitLayout, this, mViewHost, insetsState); 127 } 128 129 /** 130 * Releases the surface control of the current {@link DividerView} and tear down the view 131 * hierarchy. 132 */ release()133 void release() { 134 if (mDividerView != null) { 135 mDividerView = null; 136 } 137 138 if (mViewHost != null){ 139 mViewHost.release(); 140 mViewHost = null; 141 } 142 143 if (mLeash != null) { 144 new SurfaceControl.Transaction().remove(mLeash).apply(); 145 mLeash = null; 146 } 147 } 148 setInteractive(boolean interactive)149 void setInteractive(boolean interactive) { 150 if (mDividerView == null) return; 151 mDividerView.setInteractive(interactive); 152 } 153 154 /** 155 * Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not 156 * feasible. 157 */ 158 @Nullable getSurfaceControl()159 SurfaceControl getSurfaceControl() { 160 return mLeash; 161 } 162 onInsetsChanged(InsetsState insetsState)163 void onInsetsChanged(InsetsState insetsState) { 164 if (mDividerView != null) { 165 mDividerView.onInsetsChanged(insetsState, true /* animate */); 166 } 167 } 168 } 169