| // Copyright 2012 Google Inc. All Rights Reserved. |
| |
| package com.android.server.wm; |
| |
| import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; |
| |
| import android.content.Context; |
| import android.os.SystemClock; |
| import android.util.Log; |
| import android.util.Slog; |
| import android.view.Surface; |
| import android.view.WindowManager; |
| import android.view.WindowManager.LayoutParams; |
| import android.view.WindowManagerPolicy; |
| import android.view.animation.Animation; |
| import android.view.animation.AnimationUtils; |
| |
| import com.android.internal.policy.impl.PhoneWindowManager; |
| |
| /** |
| * @author cmautner@google.com (Craig Mautner) |
| * Singleton class that carries out the animations and Surface operations in a separate task |
| * on behalf of WindowManagerService. |
| */ |
| public class WindowAnimator { |
| private static final String TAG = "WindowAnimator"; |
| |
| final WindowManagerService mService; |
| final Context mContext; |
| final WindowManagerPolicy mPolicy; |
| |
| boolean mAnimating; |
| boolean mUpdateRotation; |
| boolean mTokenMayBeDrawn; |
| boolean mForceHiding; |
| WindowState mWindowAnimationBackground; |
| int mWindowAnimationBackgroundColor; |
| int mAdjResult; |
| |
| int mPendingLayoutChanges; |
| |
| /** Overall window dimensions */ |
| int mDw, mDh; |
| |
| /** Interior window dimensions */ |
| int mInnerDw, mInnerDh; |
| |
| /** Time of current animation step. Reset on each iteration */ |
| long mCurrentTime; |
| |
| /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this |
| * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ |
| private int mTransactionSequence; |
| |
| /** The one and only screen rotation if one is happening */ |
| ScreenRotationAnimation mScreenRotationAnimation = null; |
| |
| WindowAnimator(final WindowManagerService service, final Context context, |
| final WindowManagerPolicy policy) { |
| mService = service; |
| mContext = context; |
| mPolicy = policy; |
| } |
| |
| private void updateWindowsAppsAndRotationAnimationsLocked() { |
| int i; |
| final int NAT = mService.mAppTokens.size(); |
| for (i=0; i<NAT; i++) { |
| final AppWindowToken appToken = mService.mAppTokens.get(i); |
| final boolean wasAnimating = appToken.animation != null; |
| if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) { |
| mAnimating = true; |
| } else if (wasAnimating) { |
| // stopped animating, do one more pass through the layout |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| } |
| |
| final int NEAT = mService.mExitingAppTokens.size(); |
| for (i=0; i<NEAT; i++) { |
| final AppWindowToken appToken = mService.mExitingAppTokens.get(i); |
| final boolean wasAnimating = appToken.animation != null; |
| if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) { |
| mAnimating = true; |
| } else if (wasAnimating) { |
| // stopped animating, do one more pass through the layout |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| } |
| |
| if (mScreenRotationAnimation != null && |
| (mScreenRotationAnimation.isAnimating() || |
| mScreenRotationAnimation.mFinishAnimReady)) { |
| if (mScreenRotationAnimation.stepAnimationLocked(mCurrentTime)) { |
| mUpdateRotation = false; |
| mAnimating = true; |
| } else { |
| mUpdateRotation = true; |
| mScreenRotationAnimation.kill(); |
| mScreenRotationAnimation = null; |
| } |
| } |
| } |
| |
| private void updateWindowsAndWallpaperLocked() { |
| ++mTransactionSequence; |
| |
| for (int i = mService.mWindows.size() - 1; i >= 0; i--) { |
| WindowState w = mService.mWindows.get(i); |
| |
| final WindowManager.LayoutParams attrs = w.mAttrs; |
| |
| if (w.mSurface != null) { |
| // Take care of the window being ready to display. |
| if (w.commitFinishDrawingLocked(mCurrentTime)) { |
| if ((w.mAttrs.flags |
| & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { |
| if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG, |
| "First draw done in potential wallpaper target " + w); |
| mService.mInnerFields.mWallpaperMayChange = true; |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| } |
| |
| // If the window has moved due to its containing |
| // content frame changing, then we'd like to animate |
| // it. The checks here are ordered by what is least |
| // likely to be true first. |
| if (w.shouldAnimateMove()) { |
| // Frame has moved, containing content frame |
| // has also moved, and we're not currently animating... |
| // let's do something. |
| Animation a = AnimationUtils.loadAnimation(mContext, |
| com.android.internal.R.anim.window_move_from_decor); |
| w.setAnimation(a); |
| w.mAnimDw = w.mLastFrame.left - w.mFrame.left; |
| w.mAnimDh = w.mLastFrame.top - w.mFrame.top; |
| } else { |
| w.mAnimDw = mInnerDw; |
| w.mAnimDh = mInnerDh; |
| } |
| |
| final boolean wasAnimating = w.mWasAnimating; |
| final boolean nowAnimating = w.stepAnimationLocked(mCurrentTime); |
| |
| if (WindowManagerService.DEBUG_WALLPAPER) { |
| Slog.v(TAG, w + ": wasAnimating=" + wasAnimating + |
| ", nowAnimating=" + nowAnimating); |
| } |
| |
| // If this window is animating, make a note that we have |
| // an animating window and take care of a request to run |
| // a detached wallpaper animation. |
| if (nowAnimating) { |
| if (w.mAnimation != null) { |
| if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 |
| && w.mAnimation.getDetachWallpaper()) { |
| mService.mInnerFields.mDetachedWallpaper = w; |
| } |
| if (w.mAnimation.getBackgroundColor() != 0) { |
| if (mWindowAnimationBackground == null |
| || (w.mAnimLayer < mWindowAnimationBackground.mAnimLayer)) { |
| mWindowAnimationBackground = w; |
| mWindowAnimationBackgroundColor = |
| w.mAnimation.getBackgroundColor(); |
| } |
| } |
| } |
| mAnimating = true; |
| } |
| |
| // If this window's app token is running a detached wallpaper |
| // animation, make a note so we can ensure the wallpaper is |
| // displayed behind it. |
| if (w.mAppToken != null && w.mAppToken.animation != null |
| && w.mAppToken.animating) { |
| if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 |
| && w.mAppToken.animation.getDetachWallpaper()) { |
| mService.mInnerFields.mDetachedWallpaper = w; |
| } |
| if (w.mAppToken.animation.getBackgroundColor() != 0) { |
| if (mWindowAnimationBackground == null |
| || (w.mAnimLayer < |
| mWindowAnimationBackground.mAnimLayer)) { |
| mWindowAnimationBackground = w; |
| mWindowAnimationBackgroundColor = |
| w.mAppToken.animation.getBackgroundColor(); |
| } |
| } |
| } |
| |
| if (wasAnimating && !w.mAnimating && mService.mWallpaperTarget == w) { |
| mService.mInnerFields.mWallpaperMayChange = true; |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| |
| if (mPolicy.doesForceHide(w, attrs)) { |
| if (!wasAnimating && nowAnimating) { |
| if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, |
| "Animation started that could impact force hide: " |
| + w); |
| mService.mInnerFields.mWallpaperForceHidingChanged = true; |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| mService.mFocusMayChange = true; |
| } else if (w.isReadyForDisplay() && w.mAnimation == null) { |
| mForceHiding = true; |
| } |
| } else if (mPolicy.canBeForceHidden(w, attrs)) { |
| boolean changed; |
| if (mForceHiding) { |
| changed = w.hideLw(false, false); |
| if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG, |
| "Now policy hidden: " + w); |
| } else { |
| changed = w.showLw(false, false); |
| if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG, |
| "Now policy shown: " + w); |
| if (changed) { |
| if (mService.mInnerFields.mWallpaperForceHidingChanged |
| && w.isVisibleNow() /*w.isReadyForDisplay()*/) { |
| // Assume we will need to animate. If |
| // we don't (because the wallpaper will |
| // stay with the lock screen), then we will |
| // clean up later. |
| Animation a = mPolicy.createForceHideEnterAnimation(); |
| if (a != null) { |
| w.setAnimation(a); |
| } |
| } |
| if (mCurrentFocus == null || mCurrentFocus.mLayer < w.mLayer) { |
| // We are showing on to of the current |
| // focus, so re-evaluate focus to make |
| // sure it is correct. |
| mService.mFocusMayChange = true; |
| } |
| } |
| } |
| if (changed && (attrs.flags |
| & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { |
| mService.mInnerFields.mWallpaperMayChange = true; |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; |
| } |
| } |
| } |
| |
| final AppWindowToken atoken = w.mAppToken; |
| if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) { |
| if (atoken.lastTransactionSequence != mTransactionSequence) { |
| atoken.lastTransactionSequence = mTransactionSequence; |
| atoken.numInterestingWindows = atoken.numDrawnWindows = 0; |
| atoken.startingDisplayed = false; |
| } |
| if ((w.isOnScreen() || w.mAttrs.type |
| == WindowManager.LayoutParams.TYPE_BASE_APPLICATION) |
| && !w.mExiting && !w.mDestroying) { |
| if (WindowManagerService.DEBUG_VISIBILITY || |
| WindowManagerService.DEBUG_ORIENTATION) { |
| Slog.v(TAG, "Eval win " + w + ": isDrawn=" |
| + w.isDrawnLw() |
| + ", isAnimating=" + w.isAnimating()); |
| if (!w.isDrawnLw()) { |
| Slog.v(TAG, "Not displayed: s=" + w.mSurface |
| + " pv=" + w.mPolicyVisibility |
| + " dp=" + w.mDrawPending |
| + " cdp=" + w.mCommitDrawPending |
| + " ah=" + w.mAttachedHidden |
| + " th=" + atoken.hiddenRequested |
| + " a=" + w.mAnimating); |
| } |
| } |
| if (w != atoken.startingWindow) { |
| if (!atoken.freezingScreen || !w.mAppFreezing) { |
| atoken.numInterestingWindows++; |
| if (w.isDrawnLw()) { |
| atoken.numDrawnWindows++; |
| if (WindowManagerService.DEBUG_VISIBILITY || |
| WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG, |
| "tokenMayBeDrawn: " + atoken |
| + " freezingScreen=" + atoken.freezingScreen |
| + " mAppFreezing=" + w.mAppFreezing); |
| mTokenMayBeDrawn = true; |
| } |
| } |
| } else if (w.isDrawnLw()) { |
| atoken.startingDisplayed = true; |
| } |
| } |
| } else if (w.mReadyToShow) { |
| w.performShowLocked(); |
| mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; |
| } |
| if (atoken != null && atoken.thumbnail != null) { |
| if (atoken.thumbnailTransactionSeq != mTransactionSequence) { |
| atoken.thumbnailTransactionSeq = mTransactionSequence; |
| atoken.thumbnailLayer = 0; |
| } |
| if (atoken.thumbnailLayer < w.mAnimLayer) { |
| atoken.thumbnailLayer = w.mAnimLayer; |
| } |
| } |
| } // end forall windows |
| } |
| |
| private void testTokenMayBeDrawnLocked() { |
| // See if any windows have been drawn, so they (and others |
| // associated with them) can now be shown. |
| final int NT = mService.mAppTokens.size(); |
| for (int i=0; i<NT; i++) { |
| AppWindowToken wtoken = mService.mAppTokens.get(i); |
| if (wtoken.freezingScreen) { |
| int numInteresting = wtoken.numInterestingWindows; |
| if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { |
| if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, |
| "allDrawn: " + wtoken |
| + " interesting=" + numInteresting |
| + " drawn=" + wtoken.numDrawnWindows); |
| wtoken.showAllWindowsLocked(); |
| mService.unsetAppFreezingScreenLocked(wtoken, false, true); |
| if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG, |
| "Setting mOrientationChangeComplete=true because wtoken " |
| + wtoken + " numInteresting=" + numInteresting |
| + " numDrawn=" + wtoken.numDrawnWindows); |
| mService.mInnerFields.mOrientationChangeComplete = true; |
| } |
| } else if (!wtoken.allDrawn) { |
| int numInteresting = wtoken.numInterestingWindows; |
| if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { |
| if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, |
| "allDrawn: " + wtoken |
| + " interesting=" + numInteresting |
| + " drawn=" + wtoken.numDrawnWindows); |
| wtoken.allDrawn = true; |
| mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM; |
| |
| // We can now show all of the drawn windows! |
| if (!mService.mOpeningApps.contains(wtoken)) { |
| mAnimating |= wtoken.showAllWindowsLocked(); |
| } |
| } |
| } |
| } |
| } |
| |
| private void performAnimationsLocked() { |
| if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq=" |
| + mTransactionSequence + " mAnimating=" |
| + mAnimating); |
| |
| mTokenMayBeDrawn = false; |
| mService.mInnerFields.mWallpaperMayChange = false; |
| mForceHiding = false; |
| mService.mInnerFields.mDetachedWallpaper = null; |
| mWindowAnimationBackground = null; |
| mWindowAnimationBackgroundColor = 0; |
| |
| updateWindowsAndWallpaperLocked(); |
| |
| if (mTokenMayBeDrawn) { |
| testTokenMayBeDrawnLocked(); |
| } |
| |
| if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x" |
| + Integer.toHexString(mPendingLayoutChanges)); |
| } |
| |
| public void prepareSurfaceLocked(final WindowState w, final boolean recoveringMemory) { |
| if (w.mSurface == null) { |
| if (w.mOrientationChanging) { |
| if (WindowManagerService.DEBUG_ORIENTATION) { |
| Slog.v(TAG, "Orientation change skips hidden " + w); |
| } |
| w.mOrientationChanging = false; |
| } |
| return; |
| } |
| |
| boolean displayed = false; |
| |
| w.computeShownFrameLocked(); |
| |
| int width, height; |
| if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { |
| // for a scaled surface, we just want to use |
| // the requested size. |
| width = w.mRequestedWidth; |
| height = w.mRequestedHeight; |
| } else { |
| width = w.mCompatFrame.width(); |
| height = w.mCompatFrame.height(); |
| } |
| |
| if (width < 1) { |
| width = 1; |
| } |
| if (height < 1) { |
| height = 1; |
| } |
| final boolean surfaceResized = w.mSurfaceW != width || w.mSurfaceH != height; |
| if (surfaceResized) { |
| w.mSurfaceW = width; |
| w.mSurfaceH = height; |
| } |
| |
| if (w.mSurfaceX != w.mShownFrame.left |
| || w.mSurfaceY != w.mShownFrame.top) { |
| try { |
| if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, |
| "POS " + w.mShownFrame.left |
| + ", " + w.mShownFrame.top, null); |
| w.mSurfaceX = w.mShownFrame.left; |
| w.mSurfaceY = w.mShownFrame.top; |
| w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top); |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Error positioning surface of " + w |
| + " pos=(" + w.mShownFrame.left |
| + "," + w.mShownFrame.top + ")", e); |
| if (!recoveringMemory) { |
| mService.reclaimSomeSurfaceMemoryLocked(w, "position", true); |
| } |
| } |
| } |
| |
| if (surfaceResized) { |
| try { |
| if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, |
| "SIZE " + width + "x" + height, null); |
| w.mSurfaceResized = true; |
| w.mSurface.setSize(width, height); |
| } catch (RuntimeException e) { |
| // If something goes wrong with the surface (such |
| // as running out of memory), don't take down the |
| // entire system. |
| Slog.e(TAG, "Error resizing surface of " + w |
| + " size=(" + width + "x" + height + ")", e); |
| if (!recoveringMemory) { |
| mService.reclaimSomeSurfaceMemoryLocked(w, "size", true); |
| } |
| } |
| } |
| |
| if (w.mAttachedHidden || !w.isReadyForDisplay()) { |
| if (!w.mLastHidden) { |
| //dump(); |
| w.mLastHidden = true; |
| if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, |
| "HIDE (performLayout)", null); |
| if (w.mSurface != null) { |
| w.mSurfaceShown = false; |
| try { |
| w.mSurface.hide(); |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Exception hiding surface in " + w); |
| } |
| } |
| } |
| // If we are waiting for this window to handle an |
| // orientation change, well, it is hidden, so |
| // doesn't really matter. Note that this does |
| // introduce a potential glitch if the window |
| // becomes unhidden before it has drawn for the |
| // new orientation. |
| if (w.mOrientationChanging) { |
| w.mOrientationChanging = false; |
| if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG, |
| "Orientation change skips hidden " + w); |
| } |
| } else if (w.mLastLayer != w.mAnimLayer |
| || w.mLastAlpha != w.mShownAlpha |
| || w.mLastDsDx != w.mDsDx |
| || w.mLastDtDx != w.mDtDx |
| || w.mLastDsDy != w.mDsDy |
| || w.mLastDtDy != w.mDtDy |
| || w.mLastHScale != w.mHScale |
| || w.mLastVScale != w.mVScale |
| || w.mLastHidden) { |
| displayed = true; |
| w.mLastAlpha = w.mShownAlpha; |
| w.mLastLayer = w.mAnimLayer; |
| w.mLastDsDx = w.mDsDx; |
| w.mLastDtDx = w.mDtDx; |
| w.mLastDsDy = w.mDsDy; |
| w.mLastDtDy = w.mDtDy; |
| w.mLastHScale = w.mHScale; |
| w.mLastVScale = w.mVScale; |
| if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, |
| "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer |
| + " matrix=[" + (w.mDsDx*w.mHScale) |
| + "," + (w.mDtDx*w.mVScale) |
| + "][" + (w.mDsDy*w.mHScale) |
| + "," + (w.mDtDy*w.mVScale) + "]", null); |
| if (w.mSurface != null) { |
| try { |
| w.mSurfaceAlpha = w.mShownAlpha; |
| w.mSurface.setAlpha(w.mShownAlpha); |
| w.mSurfaceLayer = w.mAnimLayer; |
| w.mSurface.setLayer(w.mAnimLayer); |
| w.mSurface.setMatrix( |
| w.mDsDx*w.mHScale, w.mDtDx*w.mVScale, |
| w.mDsDy*w.mHScale, w.mDtDy*w.mVScale); |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Error updating surface in " + w, e); |
| if (!recoveringMemory) { |
| mService.reclaimSomeSurfaceMemoryLocked(w, "update", true); |
| } |
| } |
| } |
| |
| if (w.mLastHidden && w.isDrawnLw() |
| && !w.mReadyToShow) { |
| if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, |
| "SHOW (performLayout)", null); |
| if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w |
| + " during relayout"); |
| if (mService.showSurfaceRobustlyLocked(w)) { |
| w.mHasDrawn = true; |
| w.mLastHidden = false; |
| } else { |
| w.mOrientationChanging = false; |
| } |
| } |
| if (w.mSurface != null) { |
| w.mToken.hasVisible = true; |
| } |
| } else { |
| displayed = true; |
| } |
| |
| if (displayed) { |
| if (w.mOrientationChanging) { |
| if (!w.isDrawnLw()) { |
| mService.mInnerFields.mOrientationChangeComplete = false; |
| if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG, |
| "Orientation continue waiting for draw in " + w); |
| } else { |
| w.mOrientationChanging = false; |
| if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG, |
| "Orientation change complete in " + w); |
| } |
| } |
| w.mToken.hasVisible = true; |
| } |
| } |
| |
| void animate() { |
| mPendingLayoutChanges = 0; |
| mCurrentTime = SystemClock.uptimeMillis(); |
| |
| // Update animations of all applications, including those |
| // associated with exiting/removed apps |
| Surface.openTransaction(); |
| |
| try { |
| updateWindowsAppsAndRotationAnimationsLocked(); |
| performAnimationsLocked(); |
| |
| // THIRD LOOP: Update the surfaces of all windows. |
| |
| if (mScreenRotationAnimation != null) { |
| mScreenRotationAnimation.updateSurfaces(); |
| } |
| |
| final int N = mService.mWindows.size(); |
| for (int i=N-1; i>=0; i--) { |
| WindowState w = mService.mWindows.get(i); |
| prepareSurfaceLocked(w, true); |
| } |
| |
| if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) { |
| mAnimating |= mService.mDimAnimator.updateSurface(mService.mInnerFields.mDimming, |
| mCurrentTime, !mService.okToDisplay()); |
| } |
| |
| if (mService.mBlackFrame != null) { |
| if (mScreenRotationAnimation != null) { |
| mService.mBlackFrame.setMatrix( |
| mScreenRotationAnimation.getEnterTransformation().getMatrix()); |
| } else { |
| mService.mBlackFrame.clearMatrix(); |
| } |
| } |
| } catch (RuntimeException e) { |
| Log.wtf(TAG, "Unhandled exception in Window Manager", e); |
| } finally { |
| Surface.closeTransaction(); |
| } |
| } |
| |
| WindowState mCurrentFocus; |
| void setCurrentFocus(WindowState currentFocus) { |
| mCurrentFocus = currentFocus; |
| } |
| |
| void setDisplayDimensions(final int curWidth, final int curHeight, |
| final int appWidth, final int appHeight) { |
| mDw = curWidth; |
| mDh = curHeight; |
| mInnerDw = appWidth; |
| mInnerDh = appHeight; |
| } |
| |
| } |