1 /* 2 * Copyright (C) 2021 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 package com.android.launcher3.taskbar; 17 18 import android.content.Context; 19 import android.graphics.Canvas; 20 import android.graphics.Paint; 21 import android.graphics.Path; 22 import android.util.AttributeSet; 23 import android.view.MotionEvent; 24 import android.view.View; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 29 import com.android.launcher3.R; 30 import com.android.launcher3.testing.TestLogging; 31 import com.android.launcher3.testing.TestProtocol; 32 import com.android.launcher3.util.TouchController; 33 import com.android.launcher3.views.BaseDragLayer; 34 import com.android.systemui.shared.system.ViewTreeObserverWrapper; 35 import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo; 36 import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener; 37 38 /** 39 * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder. 40 */ 41 public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { 42 43 private final Paint mTaskbarBackgroundPaint; 44 private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath; 45 private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets; 46 47 // Initialized in init. 48 private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks; 49 private float mLeftCornerRadius, mRightCornerRadius; 50 51 private float mTaskbarBackgroundOffset; 52 TaskbarDragLayer(@onNull Context context)53 public TaskbarDragLayer(@NonNull Context context) { 54 this(context, null); 55 } 56 TaskbarDragLayer(@onNull Context context, @Nullable AttributeSet attrs)57 public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs) { 58 this(context, attrs, 0); 59 } 60 TaskbarDragLayer(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)61 public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs, 62 int defStyleAttr) { 63 this(context, attrs, defStyleAttr, 0); 64 } 65 TaskbarDragLayer(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)66 public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs, 67 int defStyleAttr, int defStyleRes) { 68 super(context, attrs, 1 /* alphaChannelCount */); 69 mTaskbarBackgroundPaint = new Paint(); 70 mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background)); 71 mTaskbarBackgroundPaint.setAlpha(0); 72 mTaskbarBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG); 73 mTaskbarBackgroundPaint.setStyle(Paint.Style.FILL); 74 75 // Will be set in init(), but this ensures they are always non-null. 76 mInvertedLeftCornerPath = new Path(); 77 mInvertedRightCornerPath = new Path(); 78 } 79 init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks)80 public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) { 81 mControllerCallbacks = callbacks; 82 83 // Create the paths for the inverted rounded corners above the taskbar. Start with a filled 84 // square, and then subtracting out a circle from the appropriate corner. 85 mLeftCornerRadius = mActivity.getLeftCornerRadius(); 86 mRightCornerRadius = mActivity.getRightCornerRadius(); 87 Path square = new Path(); 88 square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW); 89 Path circle = new Path(); 90 circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW); 91 mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE); 92 square.reset(); 93 square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW); 94 circle.reset(); 95 circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW); 96 mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE); 97 98 recreateControllers(); 99 } 100 101 @Override recreateControllers()102 public void recreateControllers() { 103 mControllers = new TouchController[] {mActivity.getDragController()}; 104 } 105 onComputeTaskbarInsets(InsetsInfo insetsInfo)106 private void onComputeTaskbarInsets(InsetsInfo insetsInfo) { 107 if (mControllerCallbacks != null) { 108 mControllerCallbacks.updateInsetsTouchability(insetsInfo); 109 mControllerCallbacks.updateContentInsets(insetsInfo.contentInsets); 110 } 111 } 112 onDestroy()113 protected void onDestroy() { 114 ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer); 115 } 116 117 @Override onAttachedToWindow()118 protected void onAttachedToWindow() { 119 super.onAttachedToWindow(); 120 ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(), 121 mTaskbarInsetsComputer); 122 } 123 124 @Override onDetachedFromWindow()125 protected void onDetachedFromWindow() { 126 super.onDetachedFromWindow(); 127 128 onDestroy(); 129 } 130 131 @Override canFindActiveController()132 protected boolean canFindActiveController() { 133 // Unlike super class, we want to be able to find controllers when touches occur in the 134 // gesture area. For example, this allows Folder to close itself when touching the Taskbar. 135 return true; 136 } 137 138 @Override onViewRemoved(View child)139 public void onViewRemoved(View child) { 140 super.onViewRemoved(child); 141 if (mControllerCallbacks != null) { 142 mControllerCallbacks.onDragLayerViewRemoved(); 143 } 144 } 145 146 @Override dispatchDraw(Canvas canvas)147 protected void dispatchDraw(Canvas canvas) { 148 float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight() 149 * (1f - mTaskbarBackgroundOffset); 150 canvas.save(); 151 canvas.translate(0, canvas.getHeight() - backgroundHeight); 152 153 // Draw the background behind taskbar content. 154 canvas.drawRect(0, 0, canvas.getWidth(), backgroundHeight, mTaskbarBackgroundPaint); 155 156 // Draw the inverted rounded corners above the taskbar. 157 canvas.translate(0, -mLeftCornerRadius); 158 canvas.drawPath(mInvertedLeftCornerPath, mTaskbarBackgroundPaint); 159 canvas.translate(0, mLeftCornerRadius); 160 canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius); 161 canvas.drawPath(mInvertedRightCornerPath, mTaskbarBackgroundPaint); 162 163 canvas.restore(); 164 super.dispatchDraw(canvas); 165 } 166 167 /** 168 * Sets the alpha of the background color behind all the Taskbar contents. 169 * @param alpha 0 is fully transparent, 1 is fully opaque. 170 */ setTaskbarBackgroundAlpha(float alpha)171 protected void setTaskbarBackgroundAlpha(float alpha) { 172 mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255)); 173 invalidate(); 174 } 175 176 /** 177 * Sets the translation of the background color behind all the Taskbar contents. 178 * @param offset 0 is fully onscreen, 1 is fully offscreen. 179 */ setTaskbarBackgroundOffset(float offset)180 protected void setTaskbarBackgroundOffset(float offset) { 181 mTaskbarBackgroundOffset = offset; 182 invalidate(); 183 } 184 185 @Override dispatchTouchEvent(MotionEvent ev)186 public boolean dispatchTouchEvent(MotionEvent ev) { 187 TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); 188 return super.dispatchTouchEvent(ev); 189 } 190 } 191