• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package com.android.systemui.pip.phone;
18 
19 import android.content.Context;
20 import android.graphics.PixelFormat;
21 import android.graphics.Point;
22 import android.graphics.Rect;
23 import android.graphics.drawable.Drawable;
24 import android.os.VibrationEffect;
25 import android.os.Vibrator;
26 import android.view.Gravity;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.WindowManager;
30 import android.view.WindowManager.LayoutParams;
31 import android.widget.FrameLayout;
32 
33 import com.android.systemui.Interpolators;
34 import com.android.systemui.R;
35 import com.android.systemui.shared.system.WindowManagerWrapper;
36 
37 /**
38  * Displays the dismiss UI and target for floating objects.
39  */
40 public class PipDismissViewController {
41 
42     // This delay controls how long to wait before we show the target when the user first moves
43     // the PIP, to prevent the target from animating if the user just wants to fling the PIP
44     public static final int SHOW_TARGET_DELAY = 100;
45     private static final int SHOW_TARGET_DURATION = 350;
46     private static final int HIDE_TARGET_DURATION = 225;
47 
48     private Context mContext;
49     private WindowManager mWindowManager;
50     private View mDismissView;
51 
52     // Used for dismissing a bubble -- bubble should be in the target to be considered a dismiss
53     private View mTargetView;
54     private int mTargetSlop;
55     private Point mWindowSize;
56     private int[] mLoc = new int[2];
57     private boolean mIntersecting;
58     private Vibrator mVibe;
59 
PipDismissViewController(Context context)60     public PipDismissViewController(Context context) {
61         mContext = context;
62         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
63         mVibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
64     }
65 
66     /**
67      * Creates the dismiss target for showing via {@link #showDismissTarget()}.
68      */
createDismissTarget()69     public void createDismissTarget() {
70         if (mDismissView == null) {
71             // Determine sizes for the view
72             final Rect stableInsets = new Rect();
73             WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
74             mWindowSize = new Point();
75             mWindowManager.getDefaultDisplay().getRealSize(mWindowSize);
76             final int gradientHeight = mContext.getResources().getDimensionPixelSize(
77                     R.dimen.pip_dismiss_gradient_height);
78             final int bottomMargin = mContext.getResources().getDimensionPixelSize(
79                     R.dimen.pip_dismiss_text_bottom_margin);
80             mTargetSlop = mContext.getResources().getDimensionPixelSize(
81                     R.dimen.bubble_dismiss_slop);
82 
83             // Create a new view for the dismiss target
84             LayoutInflater inflater = LayoutInflater.from(mContext);
85             mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null);
86             mDismissView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
87             mDismissView.forceHasOverlappingRendering(false);
88 
89             // Set the gradient background
90             Drawable gradient = mContext.getResources().getDrawable(R.drawable.pip_dismiss_scrim);
91             gradient.setAlpha((int) (255 * 0.85f));
92             mDismissView.setBackground(gradient);
93 
94             // Adjust bottom margins of the text
95             mTargetView = mDismissView.findViewById(R.id.pip_dismiss_text);
96             FrameLayout.LayoutParams tlp = (FrameLayout.LayoutParams) mTargetView.getLayoutParams();
97             tlp.bottomMargin = stableInsets.bottom + bottomMargin;
98             mTargetView.setLayoutParams(tlp);
99 
100             // Add the target to the window
101             LayoutParams lp =  new LayoutParams(
102                     LayoutParams.MATCH_PARENT, gradientHeight,
103                     0, mWindowSize.y - gradientHeight,
104                     LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
105                     LayoutParams.FLAG_LAYOUT_IN_SCREEN
106                             | LayoutParams.FLAG_NOT_TOUCHABLE
107                             | LayoutParams.FLAG_NOT_FOCUSABLE,
108                     PixelFormat.TRANSLUCENT);
109             lp.setTitle("pip-dismiss-overlay");
110             lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
111             lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
112             mWindowManager.addView(mDismissView, lp);
113         }
114         mDismissView.animate().cancel();
115     }
116 
117 
118     /**
119      * Updates the dismiss target based on location of the view, only used for bubbles not for PIP.
120      *
121      * @return whether the view is within the dismiss target.
122      */
updateTarget(View view)123     public boolean updateTarget(View view) {
124         if (mDismissView == null) {
125             return false;
126         }
127         if (mDismissView.getAlpha() > 0) {
128             view.getLocationOnScreen(mLoc);
129             Rect viewRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + view.getWidth(),
130                     mLoc[1] + view.getHeight());
131             mTargetView.getLocationOnScreen(mLoc);
132             Rect targetRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + mTargetView.getWidth(),
133                     mLoc[1] + mTargetView.getHeight());
134             expandRect(targetRect, mTargetSlop);
135             boolean intersecting = targetRect.intersect(viewRect);
136             if (intersecting != mIntersecting) {
137                 // TODO: is this the right effect?
138                 mVibe.vibrate(VibrationEffect.get(intersecting
139                         ? VibrationEffect.EFFECT_CLICK
140                         : VibrationEffect.EFFECT_TICK));
141             }
142             mIntersecting = intersecting;
143             return intersecting;
144         }
145         return false;
146     }
147 
148     /**
149      * Shows the dismiss target.
150      */
showDismissTarget()151     public void showDismissTarget() {
152         mDismissView.animate()
153                 .alpha(1f)
154                 .setInterpolator(Interpolators.LINEAR)
155                 .setStartDelay(SHOW_TARGET_DELAY)
156                 .setDuration(SHOW_TARGET_DURATION)
157                 .start();
158     }
159 
160     /**
161      * Hides and destroys the dismiss target.
162      */
destroyDismissTarget()163     public void destroyDismissTarget() {
164         if (mDismissView != null) {
165             mDismissView.animate()
166                     .alpha(0f)
167                     .setInterpolator(Interpolators.LINEAR)
168                     .setStartDelay(0)
169                     .setDuration(HIDE_TARGET_DURATION)
170                     .withEndAction(new Runnable() {
171                         @Override
172                         public void run() {
173                             mWindowManager.removeViewImmediate(mDismissView);
174                             mDismissView = null;
175                         }
176                     })
177                     .start();
178         }
179     }
180 
expandRect(Rect outRect, int expandAmount)181     private void expandRect(Rect outRect, int expandAmount) {
182         outRect.left = Math.max(0, outRect.left - expandAmount);
183         outRect.top = Math.max(0, outRect.top - expandAmount);
184         outRect.right = Math.min(mWindowSize.x, outRect.right + expandAmount);
185         outRect.bottom = Math.min(mWindowSize.y, outRect.bottom + expandAmount);
186     }
187 }
188