2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.android
.server
.display
;
19 import com
.android
.internal
.util
.DumpUtils
;
21 import android
.content
.Context
;
22 import android
.graphics
.SurfaceTexture
;
23 import android
.hardware
.display
.DisplayManager
;
24 import android
.util
.Slog
;
25 import android
.view
.Display
;
26 import android
.view
.DisplayInfo
;
27 import android
.view
.GestureDetector
;
28 import android
.view
.Gravity
;
29 import android
.view
.LayoutInflater
;
30 import android
.view
.MotionEvent
;
31 import android
.view
.ScaleGestureDetector
;
32 import android
.view
.TextureView
;
33 import android
.view
.View
;
34 import android
.view
.WindowManager
;
35 import android
.view
.TextureView
.SurfaceTextureListener
;
36 import android
.widget
.TextView
;
38 import java
.io
.PrintWriter
;
41 * Manages an overlay window on behalf of {@link OverlayDisplayAdapter}.
43 * This object must only be accessed on the UI thread.
44 * No locks are held by this object and locks must not be held while making called into it.
47 final class OverlayDisplayWindow
implements DumpUtils
.Dump
{
48 private static final String TAG
= "OverlayDisplayWindow";
49 private static final boolean DEBUG
= false;
51 private final float INITIAL_SCALE
= 0.5f
;
52 private final float MIN_SCALE
= 0.3f
;
53 private final float MAX_SCALE
= 1.0f
;
54 private final float WINDOW_ALPHA
= 0.8f
;
56 // When true, disables support for moving and resizing the overlay.
57 // The window is made non-touchable, which makes it possible to
58 // directly interact with the content underneath.
59 private final boolean DISABLE_MOVE_AND_RESIZE
= false;
61 private final Context mContext
;
62 private final String mName
;
65 private int mDensityDpi
;
66 private final int mGravity
;
67 private final boolean mSecure
;
68 private final Listener mListener
;
69 private String mTitle
;
71 private final DisplayManager mDisplayManager
;
72 private final WindowManager mWindowManager
;
75 private final Display mDefaultDisplay
;
76 private final DisplayInfo mDefaultDisplayInfo
= new DisplayInfo();
78 private View mWindowContent
;
79 private WindowManager
.LayoutParams mWindowParams
;
80 private TextureView mTextureView
;
81 private TextView mTitleTextView
;
83 private GestureDetector mGestureDetector
;
84 private ScaleGestureDetector mScaleGestureDetector
;
86 private boolean mWindowVisible
;
89 private float mWindowScale
;
91 private float mLiveTranslationX
;
92 private float mLiveTranslationY
;
93 private float mLiveScale
= 1.0f
;
95 public OverlayDisplayWindow(Context context
, String name
,
96 int width
, int height
, int densityDpi
, int gravity
, boolean secure
,
102 mListener
= listener
;
104 mDisplayManager
= (DisplayManager
)context
.getSystemService(
105 Context
.DISPLAY_SERVICE
);
106 mWindowManager
= (WindowManager
)context
.getSystemService(
107 Context
.WINDOW_SERVICE
);
109 mDefaultDisplay
= mWindowManager
.getDefaultDisplay();
110 updateDefaultDisplayInfo();
112 resize(width
, height
, densityDpi
, false /* doLayout */);
118 if (!mWindowVisible
) {
119 mDisplayManager
.registerDisplayListener(mDisplayListener
, null);
120 if (!updateDefaultDisplayInfo()) {
121 mDisplayManager
.unregisterDisplayListener(mDisplayListener
);
126 updateWindowParams();
127 mWindowManager
.addView(mWindowContent
, mWindowParams
);
128 mWindowVisible
= true;
132 public void dismiss() {
133 if (mWindowVisible
) {
134 mDisplayManager
.unregisterDisplayListener(mDisplayListener
);
135 mWindowManager
.removeView(mWindowContent
);
136 mWindowVisible
= false;
140 public void resize(int width
, int height
, int densityDpi
) {
141 resize(width
, height
, densityDpi
, true /* doLayout */);
144 private void resize(int width
, int height
, int densityDpi
, boolean doLayout
) {
147 mDensityDpi
= densityDpi
;
148 mTitle
= mContext
.getResources().getString(
149 com
.android
.internal
.R
.string
.display_manager_overlay_display_title
,
150 mName
, mWidth
, mHeight
, mDensityDpi
);
152 mTitle
+= mContext
.getResources().getString(
153 com
.android
.internal
.R
.string
.display_manager_overlay_display_secure_suffix
);
160 public void relayout() {
161 if (mWindowVisible
) {
162 updateWindowParams();
163 mWindowManager
.updateViewLayout(mWindowContent
, mWindowParams
);
168 public void dump(PrintWriter pw
, String prefix
) {
169 pw
.println("mWindowVisible=" + mWindowVisible
);
170 pw
.println("mWindowX=" + mWindowX
);
171 pw
.println("mWindowY=" + mWindowY
);
172 pw
.println("mWindowScale=" + mWindowScale
);
173 pw
.println("mWindowParams=" + mWindowParams
);
174 if (mTextureView
!= null) {
175 pw
.println("mTextureView.getScaleX()=" + mTextureView
.getScaleX());
176 pw
.println("mTextureView.getScaleY()=" + mTextureView
.getScaleY());
178 pw
.println("mLiveTranslationX=" + mLiveTranslationX
);
179 pw
.println("mLiveTranslationY=" + mLiveTranslationY
);
180 pw
.println("mLiveScale=" + mLiveScale
);
183 private boolean updateDefaultDisplayInfo() {
184 if (!mDefaultDisplay
.getDisplayInfo(mDefaultDisplayInfo
)) {
185 Slog
.w(TAG
, "Cannot show overlay display because there is no "
186 + "default display upon which to show it.");
192 private void createWindow() {
193 LayoutInflater inflater
= LayoutInflater
.from(mContext
);
195 mWindowContent
= inflater
.inflate(
196 com
.android
.internal
.R
.layout
.overlay_display_window
, null);
197 mWindowContent
.setOnTouchListener(mOnTouchListener
);
199 mTextureView
= (TextureView
)mWindowContent
.findViewById(
200 com
.android
.internal
.R
.id
.overlay_display_window_texture
);
201 mTextureView
.setPivotX(0);
202 mTextureView
.setPivotY(0);
203 mTextureView
.getLayoutParams().width
= mWidth
;
204 mTextureView
.getLayoutParams().height
= mHeight
;
205 mTextureView
.setOpaque(false);
206 mTextureView
.setSurfaceTextureListener(mSurfaceTextureListener
);
208 mTitleTextView
= (TextView
)mWindowContent
.findViewById(
209 com
.android
.internal
.R
.id
.overlay_display_window_title
);
210 mTitleTextView
.setText(mTitle
);
212 mWindowParams
= new WindowManager
.LayoutParams(
213 WindowManager
.LayoutParams
.TYPE_DISPLAY_OVERLAY
);
214 mWindowParams
.flags
|= WindowManager
.LayoutParams
.FLAG_LAYOUT_IN_SCREEN
215 | WindowManager
.LayoutParams
.FLAG_LAYOUT_NO_LIMITS
216 | WindowManager
.LayoutParams
.FLAG_NOT_FOCUSABLE
217 | WindowManager
.LayoutParams
.FLAG_NOT_TOUCH_MODAL
218 | WindowManager
.LayoutParams
.FLAG_HARDWARE_ACCELERATED
;
220 mWindowParams
.flags
|= WindowManager
.LayoutParams
.FLAG_SECURE
;
222 if (DISABLE_MOVE_AND_RESIZE
) {
223 mWindowParams
.flags
|= WindowManager
.LayoutParams
.FLAG_NOT_TOUCHABLE
;
225 mWindowParams
.privateFlags
|=
226 WindowManager
.LayoutParams
.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
;
227 mWindowParams
.alpha
= WINDOW_ALPHA
;
228 mWindowParams
.gravity
= Gravity
.TOP
| Gravity
.LEFT
;
229 mWindowParams
.setTitle(mTitle
);
231 mGestureDetector
= new GestureDetector(mContext
, mOnGestureListener
);
232 mScaleGestureDetector
= new ScaleGestureDetector(mContext
, mOnScaleGestureListener
);
234 // Set the initial position and scale.
235 // The position and scale will be clamped when the display is first shown.
236 mWindowX
= (mGravity
& Gravity
.LEFT
) == Gravity
.LEFT ?
237 0 : mDefaultDisplayInfo
.logicalWidth
;
238 mWindowY
= (mGravity
& Gravity
.TOP
) == Gravity
.TOP ?
239 0 : mDefaultDisplayInfo
.logicalHeight
;
240 mWindowScale
= INITIAL_SCALE
;
243 private void updateWindowParams() {
244 float scale
= mWindowScale
* mLiveScale
;
245 scale
= Math
.min(scale
, (float)mDefaultDisplayInfo
.logicalWidth
/ mWidth
);
246 scale
= Math
.min(scale
, (float)mDefaultDisplayInfo
.logicalHeight
/ mHeight
);
247 scale
= Math
.max(MIN_SCALE
, Math
.min(MAX_SCALE
, scale
));
249 float offsetScale
= (scale
/ mWindowScale
- 1.0f
) * 0.5f
;
250 int width
= (int)(mWidth
* scale
);
251 int height
= (int)(mHeight
* scale
);
252 int x
= (int)(mWindowX
+ mLiveTranslationX
- width
* offsetScale
);
253 int y
= (int)(mWindowY
+ mLiveTranslationY
- height
* offsetScale
);
254 x
= Math
.max(0, Math
.min(x
, mDefaultDisplayInfo
.logicalWidth
- width
));
255 y
= Math
.max(0, Math
.min(y
, mDefaultDisplayInfo
.logicalHeight
- height
));
258 Slog
.d(TAG
, "updateWindowParams: scale=" + scale
259 + ", offsetScale=" + offsetScale
260 + ", x=" + x
+ ", y=" + y
261 + ", width=" + width
+ ", height=" + height
);
264 mTextureView
.setScaleX(scale
);
265 mTextureView
.setScaleY(scale
);
269 mWindowParams
.width
= width
;
270 mWindowParams
.height
= height
;
273 private void saveWindowParams() {
274 mWindowX
= mWindowParams
.x
;
275 mWindowY
= mWindowParams
.y
;
276 mWindowScale
= mTextureView
.getScaleX();
280 private void clearLiveState() {
281 mLiveTranslationX
= 0f
;
282 mLiveTranslationY
= 0f
;
286 private final DisplayManager
.DisplayListener mDisplayListener
=
287 new DisplayManager
.DisplayListener() {
289 public void onDisplayAdded(int displayId
) {
293 public void onDisplayChanged(int displayId
) {
294 if (displayId
== mDefaultDisplay
.getDisplayId()) {
295 if (updateDefaultDisplayInfo()) {
297 mListener
.onStateChanged(mDefaultDisplayInfo
.state
);
305 public void onDisplayRemoved(int displayId
) {
306 if (displayId
== mDefaultDisplay
.getDisplayId()) {
312 private final SurfaceTextureListener mSurfaceTextureListener
=
313 new SurfaceTextureListener() {
315 public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture
,
316 int width
, int height
) {
317 mListener
.onWindowCreated(surfaceTexture
,
318 mDefaultDisplayInfo
.getMode().getRefreshRate(),
319 mDefaultDisplayInfo
.presentationDeadlineNanos
, mDefaultDisplayInfo
.state
);
323 public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture
) {
324 mListener
.onWindowDestroyed();
329 public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture
,
330 int width
, int height
) {
334 public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture
) {
338 private final View
.OnTouchListener mOnTouchListener
= new View
.OnTouchListener() {
340 public boolean onTouch(View view
, MotionEvent event
) {
341 // Work in screen coordinates.
342 final float oldX
= event
.getX();
343 final float oldY
= event
.getY();
344 event
.setLocation(event
.getRawX(), event
.getRawY());
346 mGestureDetector
.onTouchEvent(event
);
347 mScaleGestureDetector
.onTouchEvent(event
);
349 switch (event
.getActionMasked()) {
350 case MotionEvent
.ACTION_UP
:
351 case MotionEvent
.ACTION_CANCEL
:
356 // Revert to window coordinates.
357 event
.setLocation(oldX
, oldY
);
362 private final GestureDetector
.OnGestureListener mOnGestureListener
=
363 new GestureDetector
.SimpleOnGestureListener() {
365 public boolean onScroll(MotionEvent e1
, MotionEvent e2
,
366 float distanceX
, float distanceY
) {
367 mLiveTranslationX
-= distanceX
;
368 mLiveTranslationY
-= distanceY
;
374 private final ScaleGestureDetector
.OnScaleGestureListener mOnScaleGestureListener
=
375 new ScaleGestureDetector
.SimpleOnScaleGestureListener() {
377 public boolean onScale(ScaleGestureDetector detector
) {
378 mLiveScale
*= detector
.getScaleFactor();
385 * Watches for significant changes in the overlay display window lifecycle.
387 public interface Listener
{
388 public void onWindowCreated(SurfaceTexture surfaceTexture
,
389 float refreshRate
, long presentationDeadlineNanos
, int state
);
390 public void onWindowDestroyed();
391 public void onStateChanged(int state
);