blob: 28dd0ba1ad19611075a08bf446bedb073123a770 [file] [log] [blame]
Craig Mautner764983d2012-03-22 11:37:36 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2
3package com.android.server.wm;
4
5import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
6
7import android.content.Context;
8import android.os.SystemClock;
9import android.util.Log;
10import android.util.Slog;
11import android.view.Surface;
12import android.view.WindowManager;
13import android.view.WindowManager.LayoutParams;
14import android.view.WindowManagerPolicy;
15import android.view.animation.Animation;
16import android.view.animation.AnimationUtils;
17
18import com.android.internal.policy.impl.PhoneWindowManager;
19
20/**
21 * @author cmautner@google.com (Craig Mautner)
22 * Singleton class that carries out the animations and Surface operations in a separate task
23 * on behalf of WindowManagerService.
24 */
25public class WindowAnimator {
Craig Mautnerbb1449b2012-03-23 16:11:14 -070026 private static final String TAG = "WindowAnimator";
Craig Mautner764983d2012-03-22 11:37:36 -070027
28 final WindowManagerService mService;
29 final Context mContext;
30 final WindowManagerPolicy mPolicy;
31
32 boolean mAnimating;
33 boolean mUpdateRotation;
34 boolean mTokenMayBeDrawn;
35 boolean mForceHiding;
36 WindowState mWindowAnimationBackground;
37 int mWindowAnimationBackgroundColor;
38 int mAdjResult;
39
40 int mPendingLayoutChanges;
41
42 /** Overall window dimensions */
43 int mDw, mDh;
44
45 /** Interior window dimensions */
46 int mInnerDw, mInnerDh;
47
48 /** Time of current animation step. Reset on each iteration */
49 long mCurrentTime;
50
51 /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
52 * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
53 private int mTransactionSequence;
54
55 /** The one and only screen rotation if one is happening */
56 ScreenRotationAnimation mScreenRotationAnimation = null;
57
58 WindowAnimator(final WindowManagerService service, final Context context,
59 final WindowManagerPolicy policy) {
60 mService = service;
61 mContext = context;
62 mPolicy = policy;
63 }
64
65 private void updateWindowsAppsAndRotationAnimationsLocked() {
66 int i;
67 final int NAT = mService.mAppTokens.size();
68 for (i=0; i<NAT; i++) {
69 final AppWindowToken appToken = mService.mAppTokens.get(i);
Craig Mautnerbb1449b2012-03-23 16:11:14 -070070 final boolean wasAnimating = appToken.animation != null;
Craig Mautner764983d2012-03-22 11:37:36 -070071 if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
72 mAnimating = true;
Craig Mautnerbb1449b2012-03-23 16:11:14 -070073 } else if (wasAnimating) {
74 // stopped animating, do one more pass through the layout
75 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
76 }
77 }
78
79 final int NEAT = mService.mExitingAppTokens.size();
80 for (i=0; i<NEAT; i++) {
81 final AppWindowToken appToken = mService.mExitingAppTokens.get(i);
82 final boolean wasAnimating = appToken.animation != null;
83 if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
84 mAnimating = true;
85 } else if (wasAnimating) {
86 // stopped animating, do one more pass through the layout
87 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
Craig Mautner764983d2012-03-22 11:37:36 -070088 }
89 }
90
91 if (mScreenRotationAnimation != null &&
92 (mScreenRotationAnimation.isAnimating() ||
93 mScreenRotationAnimation.mFinishAnimReady)) {
94 if (mScreenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
95 mUpdateRotation = false;
96 mAnimating = true;
97 } else {
98 mUpdateRotation = true;
99 mScreenRotationAnimation.kill();
100 mScreenRotationAnimation = null;
101 }
102 }
103 }
104
105 private void updateWindowsAndWallpaperLocked() {
106 ++mTransactionSequence;
107
108 for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
109 WindowState w = mService.mWindows.get(i);
110
111 final WindowManager.LayoutParams attrs = w.mAttrs;
112
113 if (w.mSurface != null) {
114 // Take care of the window being ready to display.
115 if (w.commitFinishDrawingLocked(mCurrentTime)) {
116 if ((w.mAttrs.flags
117 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
118 if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
119 "First draw done in potential wallpaper target " + w);
120 mService.mInnerFields.mWallpaperMayChange = true;
121 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
122 }
123 }
124
125 // If the window has moved due to its containing
126 // content frame changing, then we'd like to animate
127 // it. The checks here are ordered by what is least
128 // likely to be true first.
129 if (w.shouldAnimateMove()) {
130 // Frame has moved, containing content frame
131 // has also moved, and we're not currently animating...
132 // let's do something.
133 Animation a = AnimationUtils.loadAnimation(mContext,
134 com.android.internal.R.anim.window_move_from_decor);
135 w.setAnimation(a);
136 w.mAnimDw = w.mLastFrame.left - w.mFrame.left;
137 w.mAnimDh = w.mLastFrame.top - w.mFrame.top;
138 } else {
139 w.mAnimDw = mInnerDw;
140 w.mAnimDh = mInnerDh;
141 }
142
143 final boolean wasAnimating = w.mWasAnimating;
144 final boolean nowAnimating = w.stepAnimationLocked(mCurrentTime);
145
146 if (WindowManagerService.DEBUG_WALLPAPER) {
147 Slog.v(TAG, w + ": wasAnimating=" + wasAnimating +
148 ", nowAnimating=" + nowAnimating);
149 }
150
151 // If this window is animating, make a note that we have
152 // an animating window and take care of a request to run
153 // a detached wallpaper animation.
154 if (nowAnimating) {
155 if (w.mAnimation != null) {
156 if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
157 && w.mAnimation.getDetachWallpaper()) {
158 mService.mInnerFields.mDetachedWallpaper = w;
159 }
160 if (w.mAnimation.getBackgroundColor() != 0) {
161 if (mWindowAnimationBackground == null
162 || (w.mAnimLayer < mWindowAnimationBackground.mAnimLayer)) {
163 mWindowAnimationBackground = w;
164 mWindowAnimationBackgroundColor =
165 w.mAnimation.getBackgroundColor();
166 }
167 }
168 }
169 mAnimating = true;
170 }
171
172 // If this window's app token is running a detached wallpaper
173 // animation, make a note so we can ensure the wallpaper is
174 // displayed behind it.
175 if (w.mAppToken != null && w.mAppToken.animation != null
176 && w.mAppToken.animating) {
177 if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
178 && w.mAppToken.animation.getDetachWallpaper()) {
179 mService.mInnerFields.mDetachedWallpaper = w;
180 }
181 if (w.mAppToken.animation.getBackgroundColor() != 0) {
182 if (mWindowAnimationBackground == null
183 || (w.mAnimLayer <
184 mWindowAnimationBackground.mAnimLayer)) {
185 mWindowAnimationBackground = w;
186 mWindowAnimationBackgroundColor =
187 w.mAppToken.animation.getBackgroundColor();
188 }
189 }
190 }
191
192 if (wasAnimating && !w.mAnimating && mService.mWallpaperTarget == w) {
193 mService.mInnerFields.mWallpaperMayChange = true;
194 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
195 }
196
197 if (mPolicy.doesForceHide(w, attrs)) {
198 if (!wasAnimating && nowAnimating) {
199 if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
200 "Animation started that could impact force hide: "
201 + w);
202 mService.mInnerFields.mWallpaperForceHidingChanged = true;
203 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
204 mService.mFocusMayChange = true;
205 } else if (w.isReadyForDisplay() && w.mAnimation == null) {
206 mForceHiding = true;
207 }
208 } else if (mPolicy.canBeForceHidden(w, attrs)) {
209 boolean changed;
210 if (mForceHiding) {
211 changed = w.hideLw(false, false);
212 if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
213 "Now policy hidden: " + w);
214 } else {
215 changed = w.showLw(false, false);
216 if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
217 "Now policy shown: " + w);
218 if (changed) {
219 if (mService.mInnerFields.mWallpaperForceHidingChanged
220 && w.isVisibleNow() /*w.isReadyForDisplay()*/) {
221 // Assume we will need to animate. If
222 // we don't (because the wallpaper will
223 // stay with the lock screen), then we will
224 // clean up later.
225 Animation a = mPolicy.createForceHideEnterAnimation();
226 if (a != null) {
227 w.setAnimation(a);
228 }
229 }
230 if (mCurrentFocus == null || mCurrentFocus.mLayer < w.mLayer) {
231 // We are showing on to of the current
232 // focus, so re-evaluate focus to make
233 // sure it is correct.
234 mService.mFocusMayChange = true;
235 }
236 }
237 }
238 if (changed && (attrs.flags
239 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
240 mService.mInnerFields.mWallpaperMayChange = true;
241 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
242 }
243 }
244 }
245
246 final AppWindowToken atoken = w.mAppToken;
247 if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
248 if (atoken.lastTransactionSequence != mTransactionSequence) {
249 atoken.lastTransactionSequence = mTransactionSequence;
250 atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
251 atoken.startingDisplayed = false;
252 }
253 if ((w.isOnScreen() || w.mAttrs.type
254 == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
255 && !w.mExiting && !w.mDestroying) {
256 if (WindowManagerService.DEBUG_VISIBILITY ||
257 WindowManagerService.DEBUG_ORIENTATION) {
258 Slog.v(TAG, "Eval win " + w + ": isDrawn="
259 + w.isDrawnLw()
260 + ", isAnimating=" + w.isAnimating());
261 if (!w.isDrawnLw()) {
262 Slog.v(TAG, "Not displayed: s=" + w.mSurface
263 + " pv=" + w.mPolicyVisibility
264 + " dp=" + w.mDrawPending
265 + " cdp=" + w.mCommitDrawPending
266 + " ah=" + w.mAttachedHidden
267 + " th=" + atoken.hiddenRequested
268 + " a=" + w.mAnimating);
269 }
270 }
271 if (w != atoken.startingWindow) {
272 if (!atoken.freezingScreen || !w.mAppFreezing) {
273 atoken.numInterestingWindows++;
274 if (w.isDrawnLw()) {
275 atoken.numDrawnWindows++;
276 if (WindowManagerService.DEBUG_VISIBILITY ||
277 WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
278 "tokenMayBeDrawn: " + atoken
279 + " freezingScreen=" + atoken.freezingScreen
280 + " mAppFreezing=" + w.mAppFreezing);
281 mTokenMayBeDrawn = true;
282 }
283 }
284 } else if (w.isDrawnLw()) {
285 atoken.startingDisplayed = true;
286 }
287 }
288 } else if (w.mReadyToShow) {
289 w.performShowLocked();
290 mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
291 }
292 } // end forall windows
293 }
294
295 private void testTokenMayBeDrawnLocked() {
296 // See if any windows have been drawn, so they (and others
297 // associated with them) can now be shown.
298 final int NT = mService.mAppTokens.size();
299 for (int i=0; i<NT; i++) {
300 AppWindowToken wtoken = mService.mAppTokens.get(i);
301 if (wtoken.freezingScreen) {
302 int numInteresting = wtoken.numInterestingWindows;
303 if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
304 if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
305 "allDrawn: " + wtoken
306 + " interesting=" + numInteresting
307 + " drawn=" + wtoken.numDrawnWindows);
308 wtoken.showAllWindowsLocked();
309 mService.unsetAppFreezingScreenLocked(wtoken, false, true);
310 if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
311 "Setting mOrientationChangeComplete=true because wtoken "
312 + wtoken + " numInteresting=" + numInteresting
313 + " numDrawn=" + wtoken.numDrawnWindows);
314 mService.mInnerFields.mOrientationChangeComplete = true;
315 }
316 } else if (!wtoken.allDrawn) {
317 int numInteresting = wtoken.numInterestingWindows;
318 if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
319 if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
320 "allDrawn: " + wtoken
321 + " interesting=" + numInteresting
322 + " drawn=" + wtoken.numDrawnWindows);
323 wtoken.allDrawn = true;
324 mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
325
326 // We can now show all of the drawn windows!
327 if (!mService.mOpeningApps.contains(wtoken)) {
328 mAnimating |= wtoken.showAllWindowsLocked();
329 }
330 }
331 }
332 }
333 }
334
335 private void performAnimationsLocked() {
336 if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
337 + mTransactionSequence + " mAnimating="
338 + mAnimating);
339
340 mTokenMayBeDrawn = false;
341 mService.mInnerFields.mWallpaperMayChange = false;
342 mForceHiding = false;
343 mService.mInnerFields.mDetachedWallpaper = null;
344 mWindowAnimationBackground = null;
345 mWindowAnimationBackgroundColor = 0;
346
347 updateWindowsAndWallpaperLocked();
348
349 if (mTokenMayBeDrawn) {
350 testTokenMayBeDrawnLocked();
351 }
352
353 if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
354 + Integer.toHexString(mPendingLayoutChanges));
355 }
356
357 public void prepareSurfaceLocked(final WindowState w, final boolean recoveringMemory) {
358 if (w.mSurface == null) {
359 if (w.mOrientationChanging) {
360 if (WindowManagerService.DEBUG_ORIENTATION) {
361 Slog.v(TAG, "Orientation change skips hidden " + w);
362 }
363 w.mOrientationChanging = false;
364 }
365 return;
366 }
367
368 boolean displayed = false;
369
370 w.computeShownFrameLocked();
371
372 int width, height;
373 if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
374 // for a scaled surface, we just want to use
375 // the requested size.
376 width = w.mRequestedWidth;
377 height = w.mRequestedHeight;
378 } else {
379 width = w.mCompatFrame.width();
380 height = w.mCompatFrame.height();
381 }
382
383 if (width < 1) {
384 width = 1;
385 }
386 if (height < 1) {
387 height = 1;
388 }
389 final boolean surfaceResized = w.mSurfaceW != width || w.mSurfaceH != height;
390 if (surfaceResized) {
391 w.mSurfaceW = width;
392 w.mSurfaceH = height;
393 }
394
395 if (w.mSurfaceX != w.mShownFrame.left
396 || w.mSurfaceY != w.mShownFrame.top) {
397 try {
398 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
399 "POS " + w.mShownFrame.left
400 + ", " + w.mShownFrame.top, null);
401 w.mSurfaceX = w.mShownFrame.left;
402 w.mSurfaceY = w.mShownFrame.top;
403 w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
404 } catch (RuntimeException e) {
405 Slog.w(TAG, "Error positioning surface of " + w
406 + " pos=(" + w.mShownFrame.left
407 + "," + w.mShownFrame.top + ")", e);
408 if (!recoveringMemory) {
409 mService.reclaimSomeSurfaceMemoryLocked(w, "position", true);
410 }
411 }
412 }
413
414 if (surfaceResized) {
415 try {
416 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
417 "SIZE " + width + "x" + height, null);
418 w.mSurfaceResized = true;
419 w.mSurface.setSize(width, height);
420 } catch (RuntimeException e) {
421 // If something goes wrong with the surface (such
422 // as running out of memory), don't take down the
423 // entire system.
424 Slog.e(TAG, "Error resizing surface of " + w
425 + " size=(" + width + "x" + height + ")", e);
426 if (!recoveringMemory) {
427 mService.reclaimSomeSurfaceMemoryLocked(w, "size", true);
428 }
429 }
430 }
431
432 if (w.mAttachedHidden || !w.isReadyForDisplay()) {
433 if (!w.mLastHidden) {
434 //dump();
435 w.mLastHidden = true;
436 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
437 "HIDE (performLayout)", null);
438 if (w.mSurface != null) {
439 w.mSurfaceShown = false;
440 try {
441 w.mSurface.hide();
442 } catch (RuntimeException e) {
443 Slog.w(TAG, "Exception hiding surface in " + w);
444 }
445 }
446 }
447 // If we are waiting for this window to handle an
448 // orientation change, well, it is hidden, so
449 // doesn't really matter. Note that this does
450 // introduce a potential glitch if the window
451 // becomes unhidden before it has drawn for the
452 // new orientation.
453 if (w.mOrientationChanging) {
454 w.mOrientationChanging = false;
455 if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
456 "Orientation change skips hidden " + w);
457 }
458 } else if (w.mLastLayer != w.mAnimLayer
459 || w.mLastAlpha != w.mShownAlpha
460 || w.mLastDsDx != w.mDsDx
461 || w.mLastDtDx != w.mDtDx
462 || w.mLastDsDy != w.mDsDy
463 || w.mLastDtDy != w.mDtDy
464 || w.mLastHScale != w.mHScale
465 || w.mLastVScale != w.mVScale
466 || w.mLastHidden) {
467 displayed = true;
468 w.mLastAlpha = w.mShownAlpha;
469 w.mLastLayer = w.mAnimLayer;
470 w.mLastDsDx = w.mDsDx;
471 w.mLastDtDx = w.mDtDx;
472 w.mLastDsDy = w.mDsDy;
473 w.mLastDtDy = w.mDtDy;
474 w.mLastHScale = w.mHScale;
475 w.mLastVScale = w.mVScale;
476 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
477 "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer
478 + " matrix=[" + (w.mDsDx*w.mHScale)
479 + "," + (w.mDtDx*w.mVScale)
480 + "][" + (w.mDsDy*w.mHScale)
481 + "," + (w.mDtDy*w.mVScale) + "]", null);
482 if (w.mSurface != null) {
483 try {
484 w.mSurfaceAlpha = w.mShownAlpha;
485 w.mSurface.setAlpha(w.mShownAlpha);
486 w.mSurfaceLayer = w.mAnimLayer;
487 w.mSurface.setLayer(w.mAnimLayer);
488 w.mSurface.setMatrix(
489 w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
490 w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
491 } catch (RuntimeException e) {
492 Slog.w(TAG, "Error updating surface in " + w, e);
493 if (!recoveringMemory) {
494 mService.reclaimSomeSurfaceMemoryLocked(w, "update", true);
495 }
496 }
497 }
498
499 if (w.mLastHidden && w.isDrawnLw()
500 && !w.mReadyToShow) {
501 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
502 "SHOW (performLayout)", null);
503 if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
504 + " during relayout");
505 if (mService.showSurfaceRobustlyLocked(w)) {
506 w.mHasDrawn = true;
507 w.mLastHidden = false;
508 } else {
509 w.mOrientationChanging = false;
510 }
511 }
512 if (w.mSurface != null) {
513 w.mToken.hasVisible = true;
514 }
515 } else {
516 displayed = true;
517 }
518
519 if (displayed) {
520 if (w.mOrientationChanging) {
521 if (!w.isDrawnLw()) {
522 mService.mInnerFields.mOrientationChangeComplete = false;
523 if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
524 "Orientation continue waiting for draw in " + w);
525 } else {
526 w.mOrientationChanging = false;
527 if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
528 "Orientation change complete in " + w);
529 }
530 }
531 w.mToken.hasVisible = true;
532 }
533 }
534
535 void animate() {
Craig Mautnerbb1449b2012-03-23 16:11:14 -0700536 mPendingLayoutChanges = 0;
Craig Mautner764983d2012-03-22 11:37:36 -0700537 mCurrentTime = SystemClock.uptimeMillis();
538
539 // Update animations of all applications, including those
540 // associated with exiting/removed apps
541 Surface.openTransaction();
542
543 try {
544 updateWindowsAppsAndRotationAnimationsLocked();
545 performAnimationsLocked();
546
547 // THIRD LOOP: Update the surfaces of all windows.
548
549 if (mScreenRotationAnimation != null) {
550 mScreenRotationAnimation.updateSurfaces();
551 }
552
553 final int N = mService.mWindows.size();
554 for (int i=N-1; i>=0; i--) {
555 WindowState w = mService.mWindows.get(i);
556 prepareSurfaceLocked(w, true);
557 }
558
559 if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) {
560 mAnimating |= mService.mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
561 mCurrentTime, !mService.okToDisplay());
562 }
563
564 if (mService.mBlackFrame != null) {
565 if (mScreenRotationAnimation != null) {
566 mService.mBlackFrame.setMatrix(
567 mScreenRotationAnimation.getEnterTransformation().getMatrix());
568 } else {
569 mService.mBlackFrame.clearMatrix();
570 }
571 }
572 } catch (RuntimeException e) {
573 Log.wtf(TAG, "Unhandled exception in Window Manager", e);
574 } finally {
575 Surface.closeTransaction();
576 }
577 }
578
579 WindowState mCurrentFocus;
580 void setCurrentFocus(WindowState currentFocus) {
581 mCurrentFocus = currentFocus;
582 }
583
584 void setDisplayDimensions(final int curWidth, final int curHeight,
585 final int appWidth, final int appHeight) {
586 mDw = curWidth;
587 mDh = curHeight;
588 mInnerDw = appWidth;
589 mInnerDh = appHeight;
590 }
591
592}