• 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.model.data.ItemInfo.NO_MATCHING_ID;
19 import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView;
20 import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
21 
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.util.AttributeSet;
29 import android.view.MotionEvent;
30 import android.view.SurfaceHolder;
31 import android.view.SurfaceView;
32 import android.view.View;
33 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
34 
35 import androidx.annotation.NonNull;
36 
37 import com.android.launcher3.AbstractFloatingView;
38 import com.android.launcher3.GestureNavContract;
39 import com.android.launcher3.Insettable;
40 import com.android.launcher3.Launcher;
41 import com.android.launcher3.R;
42 import com.android.launcher3.util.Executors;
43 import com.android.launcher3.util.window.RefreshRateTracker;
44 
45 /**
46  * Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes
47  * the surfaceHandle to the {@link GestureNavContract}.
48  */
49 public class FloatingSurfaceView extends AbstractFloatingView implements
50         OnGlobalLayoutListener, Insettable, SurfaceHolder.Callback2 {
51 
52     private final RectF mTmpPosition = new RectF();
53 
54     private final Launcher mLauncher;
55     private final RectF mIconPosition = new RectF();
56 
57     private final Rect mIconBounds = new Rect();
58     private final Picture mPicture = new Picture();
59     private final Runnable mRemoveViewRunnable = this::removeViewFromParent;
60 
61     private final SurfaceView mSurfaceView;
62 
63     private View mIcon;
64     private GestureNavContract mContract;
65 
FloatingSurfaceView(Context context)66     public FloatingSurfaceView(Context context) {
67         this(context, null);
68     }
69 
FloatingSurfaceView(Context context, AttributeSet attrs)70     public FloatingSurfaceView(Context context, AttributeSet attrs) {
71         this(context, attrs, 0);
72     }
73 
FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr)74     public FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
75         super(context, attrs, defStyleAttr);
76         mLauncher = Launcher.getLauncher(context);
77 
78         mSurfaceView = new SurfaceView(context);
79         mSurfaceView.setZOrderOnTop(true);
80 
81         mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
82         mSurfaceView.getHolder().addCallback(this);
83         mIsOpen = true;
84         addView(mSurfaceView);
85     }
86 
87     @Override
handleClose(boolean animate)88     protected void handleClose(boolean animate) {
89         setCurrentIconVisible(true);
90         mLauncher.getViewCache().recycleView(R.layout.floating_surface_view, this);
91         mContract = null;
92         mIcon = null;
93         mIsOpen = false;
94 
95         // Remove after some time, to avoid flickering
96         Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
97                 RefreshRateTracker.getSingleFrameMs(mLauncher));
98     }
99 
removeViewFromParent()100     private void removeViewFromParent() {
101         mPicture.beginRecording(1, 1);
102         mPicture.endRecording();
103         mLauncher.getDragLayer().removeViewInLayout(this);
104     }
105 
removeViewImmediate()106     private void removeViewImmediate() {
107         // Cancel any pending remove
108         Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(mRemoveViewRunnable);
109         removeViewFromParent();
110     }
111 
112     /**
113      * Shows the surfaceView for the provided contract
114      */
show(Launcher launcher, GestureNavContract contract)115     public static void show(Launcher launcher, GestureNavContract contract) {
116         FloatingSurfaceView view = launcher.getViewCache().getView(R.layout.floating_surface_view,
117                 launcher, launcher.getDragLayer());
118         view.mContract = contract;
119         view.mIsOpen = true;
120 
121         view.removeViewImmediate();
122         launcher.getDragLayer().addView(view);
123     }
124 
125     @Override
isOfType(int type)126     protected boolean isOfType(int type) {
127         return (type & TYPE_ICON_SURFACE) != 0;
128     }
129 
130     @Override
onControllerInterceptTouchEvent(MotionEvent ev)131     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
132         close(false);
133         removeViewImmediate();
134         return false;
135     }
136 
137     @Override
onAttachedToWindow()138     protected void onAttachedToWindow() {
139         super.onAttachedToWindow();
140         getViewTreeObserver().addOnGlobalLayoutListener(this);
141         updateIconLocation();
142     }
143 
144     @Override
onDetachedFromWindow()145     protected void onDetachedFromWindow() {
146         super.onDetachedFromWindow();
147         getViewTreeObserver().removeOnGlobalLayoutListener(this);
148         setCurrentIconVisible(true);
149     }
150 
151     @Override
onGlobalLayout()152     public void onGlobalLayout() {
153         updateIconLocation();
154     }
155 
156     @Override
setInsets(Rect insets)157     public void setInsets(Rect insets) { }
158 
updateIconLocation()159     private void updateIconLocation() {
160         if (mContract == null) {
161             return;
162         }
163         View icon = mLauncher.getFirstMatchForAppClose(NO_MATCHING_ID,
164                 mContract.componentName.getPackageName(), mContract.user,
165                 false /* supportsAllAppsState */);
166 
167         boolean iconChanged = mIcon != icon;
168         if (iconChanged) {
169             setCurrentIconVisible(true);
170             mIcon = icon;
171             setCurrentIconVisible(false);
172         }
173 
174         if (icon != null && icon.isAttachedToWindow()) {
175             getLocationBoundsForView(mLauncher, icon, false, mTmpPosition, mIconBounds);
176 
177             if (!mTmpPosition.equals(mIconPosition)) {
178                 mIconPosition.set(mTmpPosition);
179 
180                 LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
181                 lp.width = Math.round(mIconPosition.width());
182                 lp.height = Math.round(mIconPosition.height());
183                 lp.leftMargin = Math.round(mIconPosition.left);
184                 lp.topMargin = Math.round(mIconPosition.top);
185             }
186         }
187 
188         sendIconInfo();
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) {
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