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 static android.view.KeyEvent.ACTION_UP; 19 import static android.view.KeyEvent.KEYCODE_BACK; 20 21 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; 22 23 import android.content.Context; 24 import android.graphics.Canvas; 25 import android.graphics.RectF; 26 import android.media.permission.SafeCloseable; 27 import android.util.AttributeSet; 28 import android.util.FloatProperty; 29 import android.view.KeyEvent; 30 import android.view.MotionEvent; 31 import android.view.View; 32 import android.view.ViewTreeObserver; 33 import android.view.WindowInsets; 34 35 import androidx.annotation.NonNull; 36 import androidx.annotation.Nullable; 37 import androidx.core.graphics.Insets; 38 import androidx.core.view.WindowInsetsCompat; 39 40 import com.android.app.viewcapture.ViewCaptureFactory; 41 import com.android.launcher3.AbstractFloatingView; 42 import com.android.launcher3.testing.TestLogging; 43 import com.android.launcher3.testing.shared.TestProtocol; 44 import com.android.launcher3.util.MultiPropertyFactory; 45 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; 46 import com.android.launcher3.views.BaseDragLayer; 47 48 /** 49 * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder. 50 */ 51 public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> { 52 53 private static final int INDEX_ALL_OTHER_STATES = 0; 54 private static final int INDEX_STASH_ANIM = 1; 55 private static final int INDEX_COUNT = 2; 56 57 private static final FloatProperty<TaskbarDragLayer> BG_ALPHA = 58 new FloatProperty<>("taskbarBgAlpha") { 59 @Override 60 public void setValue(TaskbarDragLayer dragLayer, float alpha) { 61 dragLayer.mBackgroundRenderer.getPaint().setAlpha((int) (alpha * 255)); 62 dragLayer.invalidate(); 63 } 64 65 @Override 66 public Float get(TaskbarDragLayer dragLayer) { 67 return dragLayer.mBackgroundRenderer.getPaint().getAlpha() / 255f; 68 } 69 }; 70 71 72 private final TaskbarBackgroundRenderer mBackgroundRenderer; 73 private final ViewTreeObserver.OnComputeInternalInsetsListener mTaskbarInsetsComputer = 74 this::onComputeTaskbarInsets; 75 76 // Initialized in init. 77 private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks; 78 private SafeCloseable mViewCaptureCloseable; 79 80 private float mTaskbarBackgroundOffset; 81 private float mTaskbarBackgroundProgress; 82 private boolean mIsAnimatingTaskbarPinning = false; 83 84 private final MultiPropertyFactory<TaskbarDragLayer> mTaskbarBackgroundAlpha; 85 TaskbarDragLayer(@onNull Context context)86 public TaskbarDragLayer(@NonNull Context context) { 87 this(context, null); 88 } 89 TaskbarDragLayer(@onNull Context context, @Nullable AttributeSet attrs)90 public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs) { 91 this(context, attrs, 0); 92 } 93 TaskbarDragLayer(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)94 public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs, 95 int defStyleAttr) { 96 this(context, attrs, defStyleAttr, 0); 97 } 98 TaskbarDragLayer(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)99 public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs, 100 int defStyleAttr, int defStyleRes) { 101 super(context, attrs, 1 /* alphaChannelCount */); 102 mBackgroundRenderer = new TaskbarBackgroundRenderer(mContainer); 103 104 mTaskbarBackgroundAlpha = new MultiPropertyFactory<>(this, BG_ALPHA, INDEX_COUNT, 105 (a, b) -> a * b, 1f); 106 mTaskbarBackgroundAlpha.get(INDEX_ALL_OTHER_STATES).setValue(0); 107 } 108 init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks)109 public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) { 110 mControllerCallbacks = callbacks; 111 mBackgroundRenderer.updateStashedHandleWidth(mContainer, getResources()); 112 recreateControllers(); 113 } 114 115 @Override onApplyWindowInsets(WindowInsets insets)116 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 117 if (insets != null) { 118 WindowInsetsCompat insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets, this); 119 Insets imeInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.ime()); 120 if (imeInsets != null) { 121 mControllerCallbacks.onImeInsetChanged(); 122 } 123 } 124 return insets; 125 } 126 127 @Override recreateControllers()128 public void recreateControllers() { 129 mControllers = mControllerCallbacks.getTouchControllers(); 130 } 131 onComputeTaskbarInsets(ViewTreeObserver.InternalInsetsInfo insetsInfo)132 private void onComputeTaskbarInsets(ViewTreeObserver.InternalInsetsInfo insetsInfo) { 133 if (mControllerCallbacks != null) { 134 mControllerCallbacks.updateInsetsTouchability(insetsInfo); 135 } 136 } 137 onDestroy(boolean forceDestroy)138 protected void onDestroy(boolean forceDestroy) { 139 if (forceDestroy) { 140 getViewTreeObserver().removeOnComputeInternalInsetsListener(mTaskbarInsetsComputer); 141 } 142 } 143 onDestroy()144 protected void onDestroy() { 145 onDestroy(!ENABLE_TASKBAR_NAVBAR_UNIFICATION); 146 } 147 148 @Override onAttachedToWindow()149 protected void onAttachedToWindow() { 150 super.onAttachedToWindow(); 151 getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer); 152 mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext()) 153 .startCapture(getRootView(), ".Taskbar"); 154 } 155 156 @Override onDetachedFromWindow()157 protected void onDetachedFromWindow() { 158 super.onDetachedFromWindow(); 159 mViewCaptureCloseable.close(); 160 onDestroy(true); 161 } 162 163 @Override isEventWithinSystemGestureRegion(MotionEvent ev)164 protected boolean isEventWithinSystemGestureRegion(MotionEvent ev) { 165 final float x = ev.getX(); 166 final float y = ev.getY(); 167 168 return x >= mSystemGestureRegion.left && x < getWidth() - mSystemGestureRegion.right 169 && y >= mSystemGestureRegion.top; 170 } 171 172 @Override canFindActiveController()173 protected boolean canFindActiveController() { 174 // Unlike super class, we want to be able to find controllers when touches occur in the 175 // gesture area. For example, this allows Folder to close itself when touching the Taskbar. 176 return true; 177 } 178 179 @Override onViewRemoved(View child)180 public void onViewRemoved(View child) { 181 super.onViewRemoved(child); 182 if (mControllerCallbacks != null) { 183 mControllerCallbacks.onDragLayerViewRemoved(); 184 } 185 } 186 187 @Override dispatchDraw(Canvas canvas)188 protected void dispatchDraw(Canvas canvas) { 189 if (mContainer.isDestroyed()) return; 190 float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight() 191 * (1f - mTaskbarBackgroundOffset); 192 mBackgroundRenderer.setBackgroundHeight(backgroundHeight); 193 mBackgroundRenderer.setBackgroundProgress(mTaskbarBackgroundProgress); 194 mBackgroundRenderer.draw(canvas); 195 super.dispatchDraw(canvas); 196 mControllerCallbacks.drawDebugUi(canvas); 197 } 198 199 /** 200 * Sets animation boolean when taskbar pinning animation starts or stops. 201 */ setAnimatingTaskbarPinning(boolean animatingTaskbarPinning)202 public void setAnimatingTaskbarPinning(boolean animatingTaskbarPinning) { 203 mIsAnimatingTaskbarPinning = animatingTaskbarPinning; 204 mBackgroundRenderer.setAnimatingPinning(mIsAnimatingTaskbarPinning); 205 } 206 getBackgroundRendererAlpha()207 protected MultiProperty getBackgroundRendererAlpha() { 208 return mTaskbarBackgroundAlpha.get(INDEX_ALL_OTHER_STATES); 209 } 210 getBackgroundRendererAlphaForStash()211 protected MultiProperty getBackgroundRendererAlphaForStash() { 212 return mTaskbarBackgroundAlpha.get(INDEX_STASH_ANIM); 213 } 214 215 /** 216 * Sets the value for taskbar background switching between persistent and transient backgrounds. 217 * @param progress 0 is transient background, 1 is persistent background. 218 */ setTaskbarBackgroundProgress(float progress)219 protected void setTaskbarBackgroundProgress(float progress) { 220 mTaskbarBackgroundProgress = progress; 221 invalidate(); 222 } 223 224 /** 225 * Sets the translation of the background color behind all the Taskbar contents. 226 * @param offset 0 is fully onscreen, 1 is fully offscreen. 227 */ setTaskbarBackgroundOffset(float offset)228 protected void setTaskbarBackgroundOffset(float offset) { 229 mTaskbarBackgroundOffset = offset; 230 invalidate(); 231 } 232 233 /** 234 * Sets the roundness of the round corner above Taskbar. 235 * @param cornerRoundness 0 has no round corner, 1 has complete round corner. 236 */ setCornerRoundness(float cornerRoundness)237 protected void setCornerRoundness(float cornerRoundness) { 238 mBackgroundRenderer.setCornerRoundness(cornerRoundness); 239 invalidate(); 240 } 241 242 /* 243 * Sets the translation of the background during the swipe up gesture. 244 */ setBackgroundTranslationYForSwipe(float translationY)245 protected void setBackgroundTranslationYForSwipe(float translationY) { 246 mBackgroundRenderer.setTranslationYForSwipe(translationY); 247 invalidate(); 248 } 249 250 /* 251 * Sets the translation of the background during the spring on stash animation. 252 */ setBackgroundTranslationYForStash(float translationY)253 protected void setBackgroundTranslationYForStash(float translationY) { 254 mBackgroundRenderer.setTranslationYForStash(translationY); 255 invalidate(); 256 } 257 setBackgroundTranslationXForBubbleBar(float translationX)258 protected void setBackgroundTranslationXForBubbleBar(float translationX) { 259 mBackgroundRenderer.setTranslationXForBubbleBar(translationX); 260 invalidate(); 261 } 262 263 /** Returns the bounds in DragLayer coordinates of where the transient background was drawn. */ getLastDrawnTransientRect()264 protected RectF getLastDrawnTransientRect() { 265 return mBackgroundRenderer.getLastDrawnTransientRect(); 266 } 267 268 @Override dispatchTouchEvent(MotionEvent ev)269 public boolean dispatchTouchEvent(MotionEvent ev) { 270 TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); 271 mControllerCallbacks.onDispatchTouchEvent(ev); 272 return super.dispatchTouchEvent(ev); 273 } 274 275 /** Called while Taskbar window is focusable, e.g. when pressing back while a folder is open */ 276 @Override dispatchKeyEvent(KeyEvent event)277 public boolean dispatchKeyEvent(KeyEvent event) { 278 if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) { 279 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer); 280 if (topView != null && topView.canHandleBack()) { 281 topView.onBackInvoked(); 282 // Handled by the floating view. 283 return true; 284 } 285 } 286 return super.dispatchKeyEvent(event); 287 } 288 289 /** 290 * Sets animation boolean when only animating persistent taskbar. 291 */ setIsAnimatingPersistentTaskbarBackground(boolean animatingPersistentTaskbarBg)292 public void setIsAnimatingPersistentTaskbarBackground(boolean animatingPersistentTaskbarBg) { 293 mBackgroundRenderer.setAnimatingPersistentTaskbar(animatingPersistentTaskbarBg); 294 } 295 296 /** 297 * Sets animation boolean when only animating transient taskbar. 298 */ setIsAnimatingTransientTaskbarBackground(boolean animatingTransientTaskbarBg)299 public void setIsAnimatingTransientTaskbarBackground(boolean animatingTransientTaskbarBg) { 300 mBackgroundRenderer.setAnimatingTransientTaskbar(animatingTransientTaskbarBg); 301 } 302 303 304 /** 305 * Sets the width percentage to inset the transient taskbar's background from the left and from 306 * the right. 307 */ setBackgroundHorizontalInsets(float insetPercentage)308 public void setBackgroundHorizontalInsets(float insetPercentage) { 309 mBackgroundRenderer.setBackgroundHorizontalInsets(insetPercentage); 310 invalidate(); 311 } 312 } 313