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