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.systemui.car.window; 18 19 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 20 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 21 22 import android.content.Context; 23 import android.graphics.PixelFormat; 24 import android.os.Binder; 25 import android.view.Gravity; 26 import android.view.LayoutInflater; 27 import android.view.MotionEvent; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.view.WindowInsets; 31 import android.view.WindowManager; 32 33 import com.android.systemui.R; 34 import com.android.systemui.dagger.SysUISingleton; 35 import com.android.systemui.statusbar.policy.ConfigurationController; 36 37 import javax.inject.Inject; 38 39 /** 40 * Controls the expansion state of the primary window which will contain all of the fullscreen sysui 41 * behavior. This window still has a collapsed state in order to watch for swipe events to expand 42 * this window for the notification panel. 43 */ 44 @SysUISingleton 45 public class SystemUIOverlayWindowController implements 46 ConfigurationController.ConfigurationListener { 47 48 /** 49 * Touch listener to get touches on the view. 50 */ 51 public interface OnTouchListener { 52 53 /** 54 * Called when a touch happens on the view. 55 */ onTouch(View v, MotionEvent event)56 void onTouch(View v, MotionEvent event); 57 } 58 59 private final Context mContext; 60 private final WindowManager mWindowManager; 61 62 private ViewGroup mBaseLayout; 63 private WindowManager.LayoutParams mLp; 64 private WindowManager.LayoutParams mLpChanged; 65 private boolean mIsAttached = false; 66 private boolean mVisible = false; 67 private boolean mFocusable = false; 68 private boolean mUsingStableInsets = false; 69 70 @Inject SystemUIOverlayWindowController( Context context, WindowManager windowManager, ConfigurationController configurationController)71 public SystemUIOverlayWindowController( 72 Context context, 73 WindowManager windowManager, 74 ConfigurationController configurationController) { 75 mContext = context; 76 mWindowManager = windowManager; 77 78 mLpChanged = new WindowManager.LayoutParams(); 79 mBaseLayout = (ViewGroup) LayoutInflater.from(context) 80 .inflate(R.layout.sysui_overlay_window, /* root= */ null, false); 81 configurationController.addCallback(this); 82 } 83 84 /** 85 * Register to {@link OnTouchListener} 86 */ registerOutsideTouchListener(OnTouchListener listener)87 public void registerOutsideTouchListener(OnTouchListener listener) { 88 mBaseLayout.setOnTouchListener(new View.OnTouchListener() { 89 @Override 90 public boolean onTouch(View v, MotionEvent event) { 91 listener.onTouch(v, event); 92 return false; 93 } 94 }); 95 } 96 97 /** Returns the base view of the primary window. */ getBaseLayout()98 public ViewGroup getBaseLayout() { 99 return mBaseLayout; 100 } 101 102 /** Returns {@code true} if the window is already attached. */ isAttached()103 public boolean isAttached() { 104 return mIsAttached; 105 } 106 107 /** Attaches the window to the window manager. */ attach()108 public void attach() { 109 if (mIsAttached) { 110 return; 111 } 112 mIsAttached = true; 113 // Now that the status bar window encompasses the sliding panel and its 114 // translucent backdrop, the entire thing is made TRANSLUCENT and is 115 // hardware-accelerated. 116 mLp = new WindowManager.LayoutParams( 117 ViewGroup.LayoutParams.MATCH_PARENT, 118 ViewGroup.LayoutParams.MATCH_PARENT, 119 WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE, 120 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 121 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 122 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 123 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 124 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, 125 PixelFormat.TRANSLUCENT); 126 mLp.token = new Binder(); 127 mLp.gravity = Gravity.TOP; 128 mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 129 mLp.setTitle("SystemUIOverlayWindow"); 130 mLp.packageName = mContext.getPackageName(); 131 mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 132 mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 133 134 mWindowManager.addView(mBaseLayout, mLp); 135 mLpChanged.copyFrom(mLp); 136 setWindowVisible(false); 137 } 138 139 /** Sets the types of insets to fit. Note: This should be rarely used. */ setFitInsetsTypes(@indowInsets.Type.InsetsType int types)140 public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) { 141 mLpChanged.setFitInsetsTypes(types); 142 mLpChanged.setFitInsetsIgnoringVisibility(mUsingStableInsets); 143 updateWindow(); 144 } 145 146 /** Sets the sides of system bar insets to fit. Note: This should be rarely used. */ setFitInsetsSides(@indowInsets.Side.InsetsSide int sides)147 public void setFitInsetsSides(@WindowInsets.Side.InsetsSide int sides) { 148 mLpChanged.setFitInsetsSides(sides); 149 mLpChanged.setFitInsetsIgnoringVisibility(mUsingStableInsets); 150 updateWindow(); 151 } 152 153 /** Sets the window to the visible state. */ setWindowVisible(boolean visible)154 public void setWindowVisible(boolean visible) { 155 mVisible = visible; 156 if (visible) { 157 mBaseLayout.setVisibility(View.VISIBLE); 158 } else { 159 mBaseLayout.setVisibility(View.INVISIBLE); 160 } 161 updateWindow(); 162 } 163 164 /** Sets the window to be focusable. */ setWindowFocusable(boolean focusable)165 public void setWindowFocusable(boolean focusable) { 166 mFocusable = focusable; 167 if (focusable) { 168 mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 169 } else { 170 mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 171 } 172 updateWindow(); 173 } 174 175 /** Sets the window to enable IME. */ setWindowNeedsInput(boolean needsInput)176 public void setWindowNeedsInput(boolean needsInput) { 177 if (needsInput) { 178 mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 179 } else { 180 mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 181 } 182 updateWindow(); 183 } 184 185 /** Returns {@code true} if the window is visible */ isWindowVisible()186 public boolean isWindowVisible() { 187 return mVisible; 188 } 189 isWindowFocusable()190 public boolean isWindowFocusable() { 191 return mFocusable; 192 } 193 setUsingStableInsets(boolean useStableInsets)194 protected void setUsingStableInsets(boolean useStableInsets) { 195 mUsingStableInsets = useStableInsets; 196 } 197 updateWindow()198 private void updateWindow() { 199 if (mLp != null && mLp.copyFrom(mLpChanged) != 0) { 200 if (isAttached()) { 201 mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 202 mWindowManager.updateViewLayout(mBaseLayout, mLp); 203 } 204 } 205 } 206 } 207