• 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.Executors;
44 import com.android.launcher3.util.window.RefreshRateTracker;
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     private View mIcon;
66     private GestureNavContract mContract;
67 
FloatingSurfaceView(Context context)68     public FloatingSurfaceView(Context context) {
69         this(context, null);
70     }
71 
FloatingSurfaceView(Context context, AttributeSet attrs)72     public FloatingSurfaceView(Context context, AttributeSet attrs) {
73         this(context, attrs, 0);
74     }
75 
FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr)76     public FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
77         super(context, attrs, defStyleAttr);
78         mLauncher = Launcher.getLauncher(context);
79 
80         mSurfaceView = new SurfaceView(context);
81         mSurfaceView.setZOrderOnTop(true);
82 
83         mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
84         mSurfaceView.getHolder().addCallback(this);
85         mIsOpen = true;
86         addView(mSurfaceView);
87     }
88 
89     @Override
handleClose(boolean animate)90     protected void handleClose(boolean animate) {
91         setCurrentIconVisible(true);
92         mLauncher.getViewCache().recycleView(R.layout.floating_surface_view, this);
93         mContract = null;
94         mIcon = null;
95         mIsOpen = false;
96 
97         // Remove after some time, to avoid flickering
98         Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
99                 RefreshRateTracker.getSingleFrameMs(mLauncher));
100     }
101 
removeViewFromParent()102     private void removeViewFromParent() {
103         mPicture.beginRecording(1, 1);
104         mPicture.endRecording();
105         mLauncher.getDragLayer().removeViewInLayout(this);
106     }
107 
removeViewImmediate()108     private void removeViewImmediate() {
109         // Cancel any pending remove
110         Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(mRemoveViewRunnable);
111         removeViewFromParent();
112     }
113 
114     /**
115      * Shows the surfaceView for the provided contract
116      */
show(Launcher launcher, GestureNavContract contract)117     public static void show(Launcher launcher, GestureNavContract contract) {
118         FloatingSurfaceView view = launcher.getViewCache().getView(R.layout.floating_surface_view,
119                 launcher, launcher.getDragLayer());
120         view.mContract = contract;
121         view.mIsOpen = true;
122 
123         view.removeViewImmediate();
124         launcher.getDragLayer().addView(view);
125     }
126 
127     @Override
isOfType(int type)128     protected boolean isOfType(int type) {
129         return (type & TYPE_ICON_SURFACE) != 0;
130     }
131 
132     @Override
onControllerInterceptTouchEvent(MotionEvent ev)133     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
134         close(false);
135         removeViewImmediate();
136         return false;
137     }
138 
139     @Override
onAttachedToWindow()140     protected void onAttachedToWindow() {
141         super.onAttachedToWindow();
142         getViewTreeObserver().addOnGlobalLayoutListener(this);
143         updateIconLocation();
144     }
145 
146     @Override
onDetachedFromWindow()147     protected void onDetachedFromWindow() {
148         super.onDetachedFromWindow();
149         getViewTreeObserver().removeOnGlobalLayoutListener(this);
150         setCurrentIconVisible(true);
151     }
152 
153     @Override
onGlobalLayout()154     public void onGlobalLayout() {
155         updateIconLocation();
156     }
157 
158     @Override
setInsets(Rect insets)159     public void setInsets(Rect insets) { }
160 
updateIconLocation()161     private void updateIconLocation() {
162         if (mContract == null) {
163             return;
164         }
165         View icon = mLauncher.getFirstMatchForAppClose(-1,
166                 mContract.componentName.getPackageName(), mContract.user,
167                 false /* supportsAllAppsState */);
168 
169         boolean iconChanged = mIcon != icon;
170         if (iconChanged) {
171             setCurrentIconVisible(true);
172             mIcon = icon;
173             setCurrentIconVisible(false);
174         }
175 
176         if (icon != null && icon.isAttachedToWindow()) {
177             getLocationBoundsForView(mLauncher, icon, false, mTmpPosition, mIconBounds);
178 
179             if (!mTmpPosition.equals(mIconPosition)) {
180                 mIconPosition.set(mTmpPosition);
181                 sendIconInfo();
182 
183                 LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
184                 lp.width = Math.round(mIconPosition.width());
185                 lp.height = Math.round(mIconPosition.height());
186                 lp.leftMargin = Math.round(mIconPosition.left);
187                 lp.topMargin = Math.round(mIconPosition.top);
188             }
189         }
190         if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) {
191             // Record the icon display
192             setCurrentIconVisible(true);
193             Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());
194             c.translate(-mIconBounds.left, -mIconBounds.top);
195             mIcon.draw(c);
196             mPicture.endRecording();
197             setCurrentIconVisible(false);
198             drawOnSurface();
199         }
200     }
201 
sendIconInfo()202     private void sendIconInfo() {
203         if (mContract != null && !mIconPosition.isEmpty()) {
204             mContract.sendEndPosition(mIconPosition, mLauncher, mSurfaceView.getSurfaceControl());
205         }
206     }
207 
208     @Override
surfaceCreated(@onNull SurfaceHolder surfaceHolder)209     public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
210         drawOnSurface();
211         sendIconInfo();
212     }
213 
214     @Override
surfaceChanged(@onNull SurfaceHolder surfaceHolder, int format, int width, int height)215     public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder,
216             int format, int width, int height) {
217         drawOnSurface();
218     }
219 
220     @Override
surfaceDestroyed(@onNull SurfaceHolder surfaceHolder)221     public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {}
222 
223     @Override
surfaceRedrawNeeded(@onNull SurfaceHolder surfaceHolder)224     public void surfaceRedrawNeeded(@NonNull SurfaceHolder surfaceHolder) {
225         drawOnSurface();
226     }
227 
drawOnSurface()228     private void drawOnSurface() {
229         SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
230 
231         Canvas c = surfaceHolder.lockHardwareCanvas();
232         if (c != null) {
233             mPicture.draw(c);
234             surfaceHolder.unlockCanvasAndPost(c);
235         }
236     }
237 
setCurrentIconVisible(boolean isVisible)238     private void setCurrentIconVisible(boolean isVisible) {
239         if (mIcon != null) {
240             setIconAndDotVisible(mIcon, isVisible);
241         }
242     }
243 }
244