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 package com.android.launcher3.views; 17 18 import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView; 19 import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible; 20 21 import android.annotation.TargetApi; 22 import android.content.Context; 23 import android.graphics.Canvas; 24 import android.graphics.Picture; 25 import android.graphics.PixelFormat; 26 import android.graphics.Rect; 27 import android.graphics.RectF; 28 import android.os.Build; 29 import android.util.AttributeSet; 30 import android.view.MotionEvent; 31 import android.view.SurfaceHolder; 32 import android.view.SurfaceView; 33 import android.view.View; 34 import android.view.ViewTreeObserver.OnGlobalLayoutListener; 35 36 import androidx.annotation.NonNull; 37 38 import com.android.launcher3.AbstractFloatingView; 39 import com.android.launcher3.GestureNavContract; 40 import com.android.launcher3.Insettable; 41 import com.android.launcher3.Launcher; 42 import com.android.launcher3.R; 43 import com.android.launcher3.util.DisplayController; 44 import com.android.launcher3.util.Executors; 45 46 /** 47 * Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes 48 * the surfaceHandle to the {@link GestureNavContract}. 49 */ 50 @TargetApi(Build.VERSION_CODES.R) 51 public class FloatingSurfaceView extends AbstractFloatingView implements 52 OnGlobalLayoutListener, Insettable, SurfaceHolder.Callback2 { 53 54 private final RectF mTmpPosition = new RectF(); 55 56 private final Launcher mLauncher; 57 private final RectF mIconPosition = new RectF(); 58 59 private final Rect mIconBounds = new Rect(); 60 private final Picture mPicture = new Picture(); 61 private final Runnable mRemoveViewRunnable = this::removeViewFromParent; 62 63 private final SurfaceView mSurfaceView; 64 65 66 private View mIcon; 67 private GestureNavContract mContract; 68 FloatingSurfaceView(Context context)69 public FloatingSurfaceView(Context context) { 70 this(context, null); 71 } 72 FloatingSurfaceView(Context context, AttributeSet attrs)73 public FloatingSurfaceView(Context context, AttributeSet attrs) { 74 this(context, attrs, 0); 75 } 76 FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr)77 public FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 78 super(context, attrs, defStyleAttr); 79 mLauncher = Launcher.getLauncher(context); 80 81 mSurfaceView = new SurfaceView(context); 82 mSurfaceView.setZOrderOnTop(true); 83 84 mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT); 85 mSurfaceView.getHolder().addCallback(this); 86 mIsOpen = true; 87 addView(mSurfaceView); 88 } 89 90 @Override handleClose(boolean animate)91 protected void handleClose(boolean animate) { 92 setCurrentIconVisible(true); 93 mLauncher.getViewCache().recycleView(R.layout.floating_surface_view, this); 94 mContract = null; 95 mIcon = null; 96 mIsOpen = false; 97 98 // Remove after some time, to avoid flickering 99 Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable, 100 DisplayController.INSTANCE.get(mLauncher).getInfo().singleFrameMs); 101 } 102 removeViewFromParent()103 private void removeViewFromParent() { 104 mPicture.beginRecording(1, 1); 105 mPicture.endRecording(); 106 mLauncher.getDragLayer().removeView(this); 107 } 108 109 /** 110 * Shows the surfaceView for the provided contract 111 */ show(Launcher launcher, GestureNavContract contract)112 public static void show(Launcher launcher, GestureNavContract contract) { 113 FloatingSurfaceView view = launcher.getViewCache().getView(R.layout.floating_surface_view, 114 launcher, launcher.getDragLayer()); 115 view.mContract = contract; 116 view.mIsOpen = true; 117 118 // Cancel any pending remove 119 Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(view.mRemoveViewRunnable); 120 view.removeViewFromParent(); 121 launcher.getDragLayer().addView(view); 122 } 123 124 @Override isOfType(int type)125 protected boolean isOfType(int type) { 126 return (type & TYPE_ICON_SURFACE) != 0; 127 } 128 129 @Override onControllerInterceptTouchEvent(MotionEvent ev)130 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 131 close(false); 132 return false; 133 } 134 135 @Override onAttachedToWindow()136 protected void onAttachedToWindow() { 137 super.onAttachedToWindow(); 138 getViewTreeObserver().addOnGlobalLayoutListener(this); 139 updateIconLocation(); 140 } 141 142 @Override onDetachedFromWindow()143 protected void onDetachedFromWindow() { 144 super.onDetachedFromWindow(); 145 getViewTreeObserver().removeOnGlobalLayoutListener(this); 146 setCurrentIconVisible(true); 147 } 148 149 @Override onGlobalLayout()150 public void onGlobalLayout() { 151 updateIconLocation(); 152 } 153 154 @Override setInsets(Rect insets)155 public void setInsets(Rect insets) { } 156 updateIconLocation()157 private void updateIconLocation() { 158 if (mContract == null) { 159 return; 160 } 161 View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(-1, 162 mContract.componentName.getPackageName(), mContract.user); 163 164 boolean iconChanged = mIcon != icon; 165 if (iconChanged) { 166 setCurrentIconVisible(true); 167 mIcon = icon; 168 setCurrentIconVisible(false); 169 } 170 171 if (icon != null && icon.isAttachedToWindow()) { 172 getLocationBoundsForView(mLauncher, icon, false, mTmpPosition, mIconBounds); 173 174 if (!mTmpPosition.equals(mIconPosition)) { 175 mIconPosition.set(mTmpPosition); 176 sendIconInfo(); 177 178 LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams(); 179 lp.width = Math.round(mIconPosition.width()); 180 lp.height = Math.round(mIconPosition.height()); 181 lp.leftMargin = Math.round(mIconPosition.left); 182 lp.topMargin = Math.round(mIconPosition.top); 183 } 184 } 185 if (iconChanged && !mIconBounds.isEmpty()) { 186 // Record the icon display 187 setCurrentIconVisible(true); 188 Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height()); 189 c.translate(-mIconBounds.left, -mIconBounds.top); 190 mIcon.draw(c); 191 mPicture.endRecording(); 192 setCurrentIconVisible(false); 193 drawOnSurface(); 194 } 195 } 196 sendIconInfo()197 private void sendIconInfo() { 198 if (mContract != null && !mIconPosition.isEmpty()) { 199 mContract.sendEndPosition(mIconPosition, mSurfaceView.getSurfaceControl()); 200 } 201 } 202 203 @Override surfaceCreated(@onNull SurfaceHolder surfaceHolder)204 public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { 205 drawOnSurface(); 206 sendIconInfo(); 207 } 208 209 @Override surfaceChanged(@onNull SurfaceHolder surfaceHolder, int format, int width, int height)210 public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, 211 int format, int width, int height) { 212 drawOnSurface(); 213 } 214 215 @Override surfaceDestroyed(@onNull SurfaceHolder surfaceHolder)216 public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {} 217 218 @Override surfaceRedrawNeeded(@onNull SurfaceHolder surfaceHolder)219 public void surfaceRedrawNeeded(@NonNull SurfaceHolder surfaceHolder) { 220 drawOnSurface(); 221 } 222 drawOnSurface()223 private void drawOnSurface() { 224 SurfaceHolder surfaceHolder = mSurfaceView.getHolder(); 225 226 Canvas c = surfaceHolder.lockHardwareCanvas(); 227 if (c != null) { 228 mPicture.draw(c); 229 surfaceHolder.unlockCanvasAndPost(c); 230 } 231 } 232 setCurrentIconVisible(boolean isVisible)233 private void setCurrentIconVisible(boolean isVisible) { 234 if (mIcon != null) { 235 setIconAndDotVisible(mIcon, isVisible); 236 } 237 } 238 } 239