• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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