1 package com.android.launcher3; 2 3 import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV; 4 import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW; 5 6 import android.annotation.TargetApi; 7 import android.app.ActivityManager; 8 import android.content.Context; 9 import android.graphics.Canvas; 10 import android.graphics.Color; 11 import android.graphics.Paint; 12 import android.graphics.Rect; 13 import android.util.AttributeSet; 14 import android.view.View; 15 import android.view.ViewDebug; 16 17 public class LauncherRootView extends InsettableFrameLayout { 18 19 private final Launcher mLauncher; 20 21 private final Paint mOpaquePaint; 22 23 @ViewDebug.ExportedProperty(category = "launcher") 24 private final Rect mConsumedInsets = new Rect(); 25 26 private View mAlignedView; 27 private WindowStateListener mWindowStateListener; 28 LauncherRootView(Context context, AttributeSet attrs)29 public LauncherRootView(Context context, AttributeSet attrs) { 30 super(context, attrs); 31 32 mOpaquePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 33 mOpaquePaint.setColor(Color.BLACK); 34 mOpaquePaint.setStyle(Paint.Style.FILL); 35 36 mLauncher = Launcher.getLauncher(context); 37 } 38 39 @Override onFinishInflate()40 protected void onFinishInflate() { 41 if (getChildCount() > 0) { 42 // LauncherRootView contains only one child, which should be aligned 43 // based on the horizontal insets. 44 mAlignedView = getChildAt(0); 45 } 46 super.onFinishInflate(); 47 } 48 49 @TargetApi(23) 50 @Override fitSystemWindows(Rect insets)51 protected boolean fitSystemWindows(Rect insets) { 52 mConsumedInsets.setEmpty(); 53 boolean drawInsetBar = false; 54 if (mLauncher.isInMultiWindowModeCompat() 55 && (insets.left > 0 || insets.right > 0 || insets.bottom > 0)) { 56 mConsumedInsets.left = insets.left; 57 mConsumedInsets.right = insets.right; 58 mConsumedInsets.bottom = insets.bottom; 59 insets = new Rect(0, insets.top, 0, 0); 60 drawInsetBar = true; 61 } else if ((insets.right > 0 || insets.left > 0) && 62 (!Utilities.ATLEAST_MARSHMALLOW || 63 getContext().getSystemService(ActivityManager.class).isLowRamDevice())) { 64 mConsumedInsets.left = insets.left; 65 mConsumedInsets.right = insets.right; 66 insets = new Rect(0, insets.top, 0, insets.bottom); 67 drawInsetBar = true; 68 } 69 70 mLauncher.getSystemUiController().updateUiState( 71 UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0); 72 73 // Update device profile before notifying th children. 74 mLauncher.getDeviceProfile().updateInsets(insets); 75 boolean resetState = !insets.equals(mInsets); 76 setInsets(insets); 77 78 if (mAlignedView != null) { 79 // Apply margins on aligned view to handle consumed insets. 80 MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams(); 81 if (lp.leftMargin != mConsumedInsets.left || lp.rightMargin != mConsumedInsets.right || 82 lp.bottomMargin != mConsumedInsets.bottom) { 83 lp.leftMargin = mConsumedInsets.left; 84 lp.rightMargin = mConsumedInsets.right; 85 lp.topMargin = mConsumedInsets.top; 86 lp.bottomMargin = mConsumedInsets.bottom; 87 mAlignedView.setLayoutParams(lp); 88 } 89 } 90 if (resetState) { 91 mLauncher.getStateManager().reapplyState(true /* cancelCurrentAnimation */); 92 } 93 94 return true; // I'll take it from here 95 } 96 97 @Override setInsets(Rect insets)98 public void setInsets(Rect insets) { 99 // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by 100 // modifying child layout params. 101 if (!insets.equals(mInsets)) { 102 super.setInsets(insets); 103 } 104 } 105 dispatchInsets()106 public void dispatchInsets() { 107 mLauncher.getDeviceProfile().updateInsets(mInsets); 108 super.setInsets(mInsets); 109 } 110 111 @Override dispatchDraw(Canvas canvas)112 protected void dispatchDraw(Canvas canvas) { 113 super.dispatchDraw(canvas); 114 115 // If the right inset is opaque, draw a black rectangle to ensure that is stays opaque. 116 if (mConsumedInsets.right > 0) { 117 int width = getWidth(); 118 canvas.drawRect(width - mConsumedInsets.right, 0, width, getHeight(), mOpaquePaint); 119 } 120 if (mConsumedInsets.left > 0) { 121 canvas.drawRect(0, 0, mConsumedInsets.left, getHeight(), mOpaquePaint); 122 } 123 if (mConsumedInsets.bottom > 0) { 124 int height = getHeight(); 125 canvas.drawRect(0, height - mConsumedInsets.bottom, getWidth(), height, mOpaquePaint); 126 } 127 } 128 setWindowStateListener(WindowStateListener listener)129 public void setWindowStateListener(WindowStateListener listener) { 130 mWindowStateListener = listener; 131 } 132 133 @Override onWindowFocusChanged(boolean hasWindowFocus)134 public void onWindowFocusChanged(boolean hasWindowFocus) { 135 super.onWindowFocusChanged(hasWindowFocus); 136 if (mWindowStateListener != null) { 137 mWindowStateListener.onWindowFocusChanged(hasWindowFocus); 138 } 139 } 140 141 @Override onWindowVisibilityChanged(int visibility)142 protected void onWindowVisibilityChanged(int visibility) { 143 super.onWindowVisibilityChanged(visibility); 144 if (mWindowStateListener != null) { 145 mWindowStateListener.onWindowVisibilityChanged(visibility); 146 } 147 } 148 149 public interface WindowStateListener { 150 onWindowFocusChanged(boolean hasFocus)151 void onWindowFocusChanged(boolean hasFocus); 152 onWindowVisibilityChanged(int visibility)153 void onWindowVisibilityChanged(int visibility); 154 } 155 }