Merge "Explode WindowManagerService."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ffae14aa6f
413
services/java/com/android/server/wm/AppWindowToken.java
Normal file
413
services/java/com/android/server/wm/AppWindowToken.java
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
|
||||
import com.android.server.wm.WindowManagerService.H;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.IApplicationToken;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Version of WindowToken that is specifically for a particular application (or
|
||||
* really activity) that is displaying windows.
|
||||
*/
|
||||
class AppWindowToken extends WindowToken {
|
||||
// Non-null only for application tokens.
|
||||
final IApplicationToken appToken;
|
||||
|
||||
// All of the windows and child windows that are included in this
|
||||
// application token. Note this list is NOT sorted!
|
||||
final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
|
||||
|
||||
int groupId = -1;
|
||||
boolean appFullscreen;
|
||||
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
|
||||
// The input dispatching timeout for this application token in nanoseconds.
|
||||
long inputDispatchingTimeoutNanos;
|
||||
|
||||
// These are used for determining when all windows associated with
|
||||
// an activity have been drawn, so they can be made visible together
|
||||
// at the same time.
|
||||
int lastTransactionSequence;
|
||||
int numInterestingWindows;
|
||||
int numDrawnWindows;
|
||||
boolean inPendingTransaction;
|
||||
boolean allDrawn;
|
||||
|
||||
// Is this token going to be hidden in a little while? If so, it
|
||||
// won't be taken into account for setting the screen orientation.
|
||||
boolean willBeHidden;
|
||||
|
||||
// Is this window's surface needed? This is almost like hidden, except
|
||||
// it will sometimes be true a little earlier: when the token has
|
||||
// been shown, but is still waiting for its app transition to execute
|
||||
// before making its windows shown.
|
||||
boolean hiddenRequested;
|
||||
|
||||
// Have we told the window clients to hide themselves?
|
||||
boolean clientHidden;
|
||||
|
||||
// Last visibility state we reported to the app token.
|
||||
boolean reportedVisible;
|
||||
|
||||
// Set to true when the token has been removed from the window mgr.
|
||||
boolean removed;
|
||||
|
||||
// Have we been asked to have this token keep the screen frozen?
|
||||
boolean freezingScreen;
|
||||
|
||||
boolean animating;
|
||||
Animation animation;
|
||||
boolean hasTransformation;
|
||||
final Transformation transformation = new Transformation();
|
||||
|
||||
// Offset to the window of all layers in the token, for use by
|
||||
// AppWindowToken animations.
|
||||
int animLayerAdjustment;
|
||||
|
||||
// Information about an application starting window if displayed.
|
||||
StartingData startingData;
|
||||
WindowState startingWindow;
|
||||
View startingView;
|
||||
boolean startingDisplayed;
|
||||
boolean startingMoved;
|
||||
boolean firstWindowDrawn;
|
||||
|
||||
// Input application handle used by the input dispatcher.
|
||||
InputApplicationHandle mInputApplicationHandle;
|
||||
|
||||
AppWindowToken(WindowManagerService _service, IApplicationToken _token) {
|
||||
super(_service, _token.asBinder(),
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION, true);
|
||||
appWindowToken = this;
|
||||
appToken = _token;
|
||||
mInputApplicationHandle = new InputApplicationHandle(this);
|
||||
lastTransactionSequence = service.mTransactionSequence-1;
|
||||
}
|
||||
|
||||
public void setAnimation(Animation anim) {
|
||||
if (WindowManagerService.localLOGV) Slog.v(
|
||||
WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
|
||||
animation = anim;
|
||||
animating = false;
|
||||
anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
|
||||
anim.scaleCurrentDuration(service.mTransitionAnimationScale);
|
||||
int zorder = anim.getZAdjustment();
|
||||
int adj = 0;
|
||||
if (zorder == Animation.ZORDER_TOP) {
|
||||
adj = WindowManagerService.TYPE_LAYER_OFFSET;
|
||||
} else if (zorder == Animation.ZORDER_BOTTOM) {
|
||||
adj = -WindowManagerService.TYPE_LAYER_OFFSET;
|
||||
}
|
||||
|
||||
if (animLayerAdjustment != adj) {
|
||||
animLayerAdjustment = adj;
|
||||
updateLayers();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDummyAnimation() {
|
||||
if (animation == null) {
|
||||
if (WindowManagerService.localLOGV) Slog.v(
|
||||
WindowManagerService.TAG, "Setting dummy animation in " + this);
|
||||
animation = WindowManagerService.sDummyAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAnimation() {
|
||||
if (animation != null) {
|
||||
animation = null;
|
||||
animating = true;
|
||||
}
|
||||
}
|
||||
|
||||
void updateLayers() {
|
||||
final int N = allAppWindows.size();
|
||||
final int adj = animLayerAdjustment;
|
||||
for (int i=0; i<N; i++) {
|
||||
WindowState w = allAppWindows.get(i);
|
||||
w.mAnimLayer = w.mLayer + adj;
|
||||
if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
|
||||
+ w.mAnimLayer);
|
||||
if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
|
||||
service.setInputMethodAnimLayerAdjustment(adj);
|
||||
}
|
||||
if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) {
|
||||
service.setWallpaperAnimLayerAdjustmentLocked(adj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendAppVisibilityToClients() {
|
||||
final int N = allAppWindows.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
WindowState win = allAppWindows.get(i);
|
||||
if (win == startingWindow && clientHidden) {
|
||||
// Don't hide the starting window.
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
|
||||
"Setting visibility of " + win + ": " + (!clientHidden));
|
||||
win.mClient.dispatchAppVisibility(!clientHidden);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showAllWindowsLocked() {
|
||||
final int NW = allAppWindows.size();
|
||||
for (int i=0; i<NW; i++) {
|
||||
WindowState w = allAppWindows.get(i);
|
||||
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
|
||||
"performing show on: " + w);
|
||||
w.performShowLocked();
|
||||
}
|
||||
}
|
||||
|
||||
// This must be called while inside a transaction.
|
||||
boolean stepAnimationLocked(long currentTime, int dw, int dh) {
|
||||
if (!service.mDisplayFrozen && service.mPolicy.isScreenOn()) {
|
||||
// We will run animations as long as the display isn't frozen.
|
||||
|
||||
if (animation == WindowManagerService.sDummyAnimation) {
|
||||
// This guy is going to animate, but not yet. For now count
|
||||
// it as not animating for purposes of scheduling transactions;
|
||||
// when it is really time to animate, this will be set to
|
||||
// a real animation and the next call will execute normally.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((allDrawn || animating || startingDisplayed) && animation != null) {
|
||||
if (!animating) {
|
||||
if (WindowManagerService.DEBUG_ANIM) Slog.v(
|
||||
WindowManagerService.TAG, "Starting animation in " + this +
|
||||
" @ " + currentTime + ": dw=" + dw + " dh=" + dh
|
||||
+ " scale=" + service.mTransitionAnimationScale
|
||||
+ " allDrawn=" + allDrawn + " animating=" + animating);
|
||||
animation.initialize(dw, dh, dw, dh);
|
||||
animation.setStartTime(currentTime);
|
||||
animating = true;
|
||||
}
|
||||
transformation.clear();
|
||||
final boolean more = animation.getTransformation(
|
||||
currentTime, transformation);
|
||||
if (WindowManagerService.DEBUG_ANIM) Slog.v(
|
||||
WindowManagerService.TAG, "Stepped animation in " + this +
|
||||
": more=" + more + ", xform=" + transformation);
|
||||
if (more) {
|
||||
// we're done!
|
||||
hasTransformation = true;
|
||||
return true;
|
||||
}
|
||||
if (WindowManagerService.DEBUG_ANIM) Slog.v(
|
||||
WindowManagerService.TAG, "Finished animation in " + this +
|
||||
" @ " + currentTime);
|
||||
animation = null;
|
||||
}
|
||||
} else if (animation != null) {
|
||||
// If the display is frozen, and there is a pending animation,
|
||||
// clear it and make sure we run the cleanup code.
|
||||
animating = true;
|
||||
animation = null;
|
||||
}
|
||||
|
||||
hasTransformation = false;
|
||||
|
||||
if (!animating) {
|
||||
return false;
|
||||
}
|
||||
|
||||
clearAnimation();
|
||||
animating = false;
|
||||
if (animLayerAdjustment != 0) {
|
||||
animLayerAdjustment = 0;
|
||||
updateLayers();
|
||||
}
|
||||
if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) {
|
||||
service.moveInputMethodWindowsIfNeededLocked(true);
|
||||
}
|
||||
|
||||
if (WindowManagerService.DEBUG_ANIM) Slog.v(
|
||||
WindowManagerService.TAG, "Animation done in " + this
|
||||
+ ": reportedVisible=" + reportedVisible);
|
||||
|
||||
transformation.clear();
|
||||
|
||||
final int N = windows.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
windows.get(i).finishExit();
|
||||
}
|
||||
updateReportedVisibilityLocked();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateReportedVisibilityLocked() {
|
||||
if (appToken == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int numInteresting = 0;
|
||||
int numVisible = 0;
|
||||
boolean nowGone = true;
|
||||
|
||||
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this);
|
||||
final int N = allAppWindows.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
WindowState win = allAppWindows.get(i);
|
||||
if (win == startingWindow || win.mAppFreezing
|
||||
|| win.mViewVisibility != View.VISIBLE
|
||||
|| win.mAttrs.type == TYPE_APPLICATION_STARTING
|
||||
|| win.mDestroying) {
|
||||
continue;
|
||||
}
|
||||
if (WindowManagerService.DEBUG_VISIBILITY) {
|
||||
Slog.v(WindowManagerService.TAG, "Win " + win + ": isDrawn="
|
||||
+ win.isDrawnLw()
|
||||
+ ", isAnimating=" + win.isAnimating());
|
||||
if (!win.isDrawnLw()) {
|
||||
Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mSurface
|
||||
+ " pv=" + win.mPolicyVisibility
|
||||
+ " dp=" + win.mDrawPending
|
||||
+ " cdp=" + win.mCommitDrawPending
|
||||
+ " ah=" + win.mAttachedHidden
|
||||
+ " th="
|
||||
+ (win.mAppToken != null
|
||||
? win.mAppToken.hiddenRequested : false)
|
||||
+ " a=" + win.mAnimating);
|
||||
}
|
||||
}
|
||||
numInteresting++;
|
||||
if (win.isDrawnLw()) {
|
||||
if (!win.isAnimating()) {
|
||||
numVisible++;
|
||||
}
|
||||
nowGone = false;
|
||||
} else if (win.isAnimating()) {
|
||||
nowGone = false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
|
||||
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "VIS " + this + ": interesting="
|
||||
+ numInteresting + " visible=" + numVisible);
|
||||
if (nowVisible != reportedVisible) {
|
||||
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(
|
||||
WindowManagerService.TAG, "Visibility changed in " + this
|
||||
+ ": vis=" + nowVisible);
|
||||
reportedVisible = nowVisible;
|
||||
Message m = service.mH.obtainMessage(
|
||||
H.REPORT_APPLICATION_TOKEN_WINDOWS,
|
||||
nowVisible ? 1 : 0,
|
||||
nowGone ? 1 : 0,
|
||||
this);
|
||||
service.mH.sendMessage(m);
|
||||
}
|
||||
}
|
||||
|
||||
WindowState findMainWindow() {
|
||||
int j = windows.size();
|
||||
while (j > 0) {
|
||||
j--;
|
||||
WindowState win = windows.get(j);
|
||||
if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
|
||||
|| win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
|
||||
return win;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
super.dump(pw, prefix);
|
||||
if (appToken != null) {
|
||||
pw.print(prefix); pw.println("app=true");
|
||||
}
|
||||
if (allAppWindows.size() > 0) {
|
||||
pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
|
||||
}
|
||||
pw.print(prefix); pw.print("groupId="); pw.print(groupId);
|
||||
pw.print(" appFullscreen="); pw.print(appFullscreen);
|
||||
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
|
||||
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
|
||||
pw.print(" clientHidden="); pw.print(clientHidden);
|
||||
pw.print(" willBeHidden="); pw.print(willBeHidden);
|
||||
pw.print(" reportedVisible="); pw.println(reportedVisible);
|
||||
if (paused || freezingScreen) {
|
||||
pw.print(prefix); pw.print("paused="); pw.print(paused);
|
||||
pw.print(" freezingScreen="); pw.println(freezingScreen);
|
||||
}
|
||||
if (numInterestingWindows != 0 || numDrawnWindows != 0
|
||||
|| inPendingTransaction || allDrawn) {
|
||||
pw.print(prefix); pw.print("numInterestingWindows=");
|
||||
pw.print(numInterestingWindows);
|
||||
pw.print(" numDrawnWindows="); pw.print(numDrawnWindows);
|
||||
pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
|
||||
pw.print(" allDrawn="); pw.println(allDrawn);
|
||||
}
|
||||
if (animating || animation != null) {
|
||||
pw.print(prefix); pw.print("animating="); pw.print(animating);
|
||||
pw.print(" animation="); pw.println(animation);
|
||||
}
|
||||
if (hasTransformation) {
|
||||
pw.print(prefix); pw.print("XForm: ");
|
||||
transformation.printShortString(pw);
|
||||
pw.println();
|
||||
}
|
||||
if (animLayerAdjustment != 0) {
|
||||
pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
|
||||
}
|
||||
if (startingData != null || removed || firstWindowDrawn) {
|
||||
pw.print(prefix); pw.print("startingData="); pw.print(startingData);
|
||||
pw.print(" removed="); pw.print(removed);
|
||||
pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn);
|
||||
}
|
||||
if (startingWindow != null || startingView != null
|
||||
|| startingDisplayed || startingMoved) {
|
||||
pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
|
||||
pw.print(" startingView="); pw.print(startingView);
|
||||
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
|
||||
pw.print(" startingMoved"); pw.println(startingMoved);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (stringName == null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("AppWindowToken{");
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append(" token="); sb.append(token); sb.append('}');
|
||||
stringName = sb.toString();
|
||||
}
|
||||
return stringName;
|
||||
}
|
||||
}
|
||||
187
services/java/com/android/server/wm/DimAnimator.java
Normal file
187
services/java/com/android/server/wm/DimAnimator.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.util.Slog;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceSession;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* DimAnimator class that controls the dim animation. This holds the surface and
|
||||
* all state used for dim animation.
|
||||
*/
|
||||
class DimAnimator {
|
||||
Surface mDimSurface;
|
||||
boolean mDimShown = false;
|
||||
float mDimCurrentAlpha;
|
||||
float mDimTargetAlpha;
|
||||
float mDimDeltaPerMs;
|
||||
long mLastDimAnimTime;
|
||||
|
||||
int mLastDimWidth, mLastDimHeight;
|
||||
|
||||
DimAnimator (SurfaceSession session) {
|
||||
if (mDimSurface == null) {
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
|
||||
+ mDimSurface + ": CREATE");
|
||||
try {
|
||||
mDimSurface = new Surface(session, 0,
|
||||
"DimSurface",
|
||||
-1, 16, 16, PixelFormat.OPAQUE,
|
||||
Surface.FX_SURFACE_DIM);
|
||||
mDimSurface.setAlpha(0.0f);
|
||||
} catch (Exception e) {
|
||||
Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the dim surface.
|
||||
*/
|
||||
void show(int dw, int dh) {
|
||||
if (!mDimShown) {
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
|
||||
dw + "x" + dh + ")");
|
||||
mDimShown = true;
|
||||
try {
|
||||
mLastDimWidth = dw;
|
||||
mLastDimHeight = dh;
|
||||
mDimSurface.setPosition(0, 0);
|
||||
mDimSurface.setSize(dw, dh);
|
||||
mDimSurface.show();
|
||||
} catch (RuntimeException e) {
|
||||
Slog.w(WindowManagerService.TAG, "Failure showing dim surface", e);
|
||||
}
|
||||
} else if (mLastDimWidth != dw || mLastDimHeight != dh) {
|
||||
mLastDimWidth = dw;
|
||||
mLastDimHeight = dh;
|
||||
mDimSurface.setSize(dw, dh);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's the dim surface's layer and update dim parameters that will be used in
|
||||
* {@link updateSurface} after all windows are examined.
|
||||
*/
|
||||
void updateParameters(Resources res, WindowState w, long currentTime) {
|
||||
mDimSurface.setLayer(w.mAnimLayer-1);
|
||||
|
||||
final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface
|
||||
+ ": layer=" + (w.mAnimLayer-1) + " target=" + target);
|
||||
if (mDimTargetAlpha != target) {
|
||||
// If the desired dim level has changed, then
|
||||
// start an animation to it.
|
||||
mLastDimAnimTime = currentTime;
|
||||
long duration = (w.mAnimating && w.mAnimation != null)
|
||||
? w.mAnimation.computeDurationHint()
|
||||
: WindowManagerService.DEFAULT_DIM_DURATION;
|
||||
if (target > mDimTargetAlpha) {
|
||||
TypedValue tv = new TypedValue();
|
||||
res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration,
|
||||
tv, true);
|
||||
if (tv.type == TypedValue.TYPE_FRACTION) {
|
||||
duration = (long)tv.getFraction((float)duration, (float)duration);
|
||||
} else if (tv.type >= TypedValue.TYPE_FIRST_INT
|
||||
&& tv.type <= TypedValue.TYPE_LAST_INT) {
|
||||
duration = tv.data;
|
||||
}
|
||||
}
|
||||
if (duration < 1) {
|
||||
// Don't divide by zero
|
||||
duration = 1;
|
||||
}
|
||||
mDimTargetAlpha = target;
|
||||
mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updating the surface's alpha. Returns true if the animation continues, or returns
|
||||
* false when the animation is finished and the dim surface is hidden.
|
||||
*/
|
||||
boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) {
|
||||
if (!dimming) {
|
||||
if (mDimTargetAlpha != 0) {
|
||||
mLastDimAnimTime = currentTime;
|
||||
mDimTargetAlpha = 0;
|
||||
mDimDeltaPerMs = (-mDimCurrentAlpha) / WindowManagerService.DEFAULT_DIM_DURATION;
|
||||
}
|
||||
}
|
||||
|
||||
boolean animating = false;
|
||||
if (mLastDimAnimTime != 0) {
|
||||
mDimCurrentAlpha += mDimDeltaPerMs
|
||||
* (currentTime-mLastDimAnimTime);
|
||||
boolean more = true;
|
||||
if (displayFrozen) {
|
||||
// If the display is frozen, there is no reason to animate.
|
||||
more = false;
|
||||
} else if (mDimDeltaPerMs > 0) {
|
||||
if (mDimCurrentAlpha > mDimTargetAlpha) {
|
||||
more = false;
|
||||
}
|
||||
} else if (mDimDeltaPerMs < 0) {
|
||||
if (mDimCurrentAlpha < mDimTargetAlpha) {
|
||||
more = false;
|
||||
}
|
||||
} else {
|
||||
more = false;
|
||||
}
|
||||
|
||||
// Do we need to continue animating?
|
||||
if (more) {
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
|
||||
+ mDimSurface + ": alpha=" + mDimCurrentAlpha);
|
||||
mLastDimAnimTime = currentTime;
|
||||
mDimSurface.setAlpha(mDimCurrentAlpha);
|
||||
animating = true;
|
||||
} else {
|
||||
mDimCurrentAlpha = mDimTargetAlpha;
|
||||
mLastDimAnimTime = 0;
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
|
||||
+ mDimSurface + ": final alpha=" + mDimCurrentAlpha);
|
||||
mDimSurface.setAlpha(mDimCurrentAlpha);
|
||||
if (!dimming) {
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface
|
||||
+ ": HIDE");
|
||||
try {
|
||||
mDimSurface.hide();
|
||||
} catch (RuntimeException e) {
|
||||
Slog.w(WindowManagerService.TAG, "Illegal argument exception hiding dim surface");
|
||||
}
|
||||
mDimShown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return animating;
|
||||
}
|
||||
|
||||
public void printTo(PrintWriter pw) {
|
||||
pw.print(" mDimShown="); pw.print(mDimShown);
|
||||
pw.print(" current="); pw.print(mDimCurrentAlpha);
|
||||
pw.print(" target="); pw.print(mDimTargetAlpha);
|
||||
pw.print(" delta="); pw.print(mDimDeltaPerMs);
|
||||
pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
|
||||
}
|
||||
}
|
||||
368
services/java/com/android/server/wm/DragState.java
Normal file
368
services/java/com/android/server/wm/DragState.java
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import com.android.server.wm.WindowManagerService.H;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.graphics.Region;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.DragEvent;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputQueue;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerPolicy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Drag/drop state
|
||||
*/
|
||||
class DragState {
|
||||
final WindowManagerService mService;
|
||||
IBinder mToken;
|
||||
Surface mSurface;
|
||||
int mFlags;
|
||||
IBinder mLocalWin;
|
||||
ClipData mData;
|
||||
ClipDescription mDataDescription;
|
||||
boolean mDragResult;
|
||||
float mCurrentX, mCurrentY;
|
||||
float mThumbOffsetX, mThumbOffsetY;
|
||||
InputChannel mServerChannel, mClientChannel;
|
||||
WindowState mTargetWindow;
|
||||
ArrayList<WindowState> mNotifiedWindows;
|
||||
boolean mDragInProgress;
|
||||
|
||||
private final Region mTmpRegion = new Region();
|
||||
|
||||
DragState(WindowManagerService service, IBinder token, Surface surface,
|
||||
int flags, IBinder localWin) {
|
||||
mService = service;
|
||||
mToken = token;
|
||||
mSurface = surface;
|
||||
mFlags = flags;
|
||||
mLocalWin = localWin;
|
||||
mNotifiedWindows = new ArrayList<WindowState>();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (mSurface != null) {
|
||||
mSurface.destroy();
|
||||
}
|
||||
mSurface = null;
|
||||
mFlags = 0;
|
||||
mLocalWin = null;
|
||||
mToken = null;
|
||||
mData = null;
|
||||
mThumbOffsetX = mThumbOffsetY = 0;
|
||||
mNotifiedWindows = null;
|
||||
}
|
||||
|
||||
void register() {
|
||||
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
|
||||
if (mClientChannel != null) {
|
||||
Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
|
||||
} else {
|
||||
InputChannel[] channels = InputChannel.openInputChannelPair("drag");
|
||||
mServerChannel = channels[0];
|
||||
mClientChannel = channels[1];
|
||||
mService.mInputManager.registerInputChannel(mServerChannel, null);
|
||||
InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler,
|
||||
mService.mH.getLooper().getQueue());
|
||||
}
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "unregistering drag input channel");
|
||||
if (mClientChannel == null) {
|
||||
Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
|
||||
} else {
|
||||
mService.mInputManager.unregisterInputChannel(mServerChannel);
|
||||
InputQueue.unregisterInputChannel(mClientChannel);
|
||||
mClientChannel.dispose();
|
||||
mServerChannel.dispose();
|
||||
mClientChannel = null;
|
||||
mServerChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
int getDragLayerLw() {
|
||||
return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
|
||||
* WindowManagerService.TYPE_LAYER_MULTIPLIER
|
||||
+ WindowManagerService.TYPE_LAYER_OFFSET;
|
||||
}
|
||||
|
||||
/* call out to each visible window/session informing it about the drag
|
||||
*/
|
||||
void broadcastDragStartedLw(final float touchX, final float touchY) {
|
||||
// Cache a base-class instance of the clip metadata so that parceling
|
||||
// works correctly in calling out to the apps.
|
||||
mDataDescription = (mData != null) ? mData.getDescription() : null;
|
||||
mNotifiedWindows.clear();
|
||||
mDragInProgress = true;
|
||||
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
|
||||
}
|
||||
|
||||
final int N = mService.mWindows.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
sendDragStartedLw(mService.mWindows.get(i), touchX, touchY, mDataDescription);
|
||||
}
|
||||
}
|
||||
|
||||
/* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
|
||||
* designated window is potentially a drop recipient. There are race situations
|
||||
* around DRAG_ENDED broadcast, so we make sure that once we've declared that
|
||||
* the drag has ended, we never send out another DRAG_STARTED for this drag action.
|
||||
*
|
||||
* This method clones the 'event' parameter if it's being delivered to the same
|
||||
* process, so it's safe for the caller to call recycle() on the event afterwards.
|
||||
*/
|
||||
private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
|
||||
ClipDescription desc) {
|
||||
// Don't actually send the event if the drag is supposed to be pinned
|
||||
// to the originating window but 'newWin' is not that window.
|
||||
if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
|
||||
final IBinder winBinder = newWin.mClient.asBinder();
|
||||
if (winBinder != mLocalWin) {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "Not dispatching local DRAG_STARTED to " + newWin);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDragInProgress && newWin.isPotentialDragTarget()) {
|
||||
DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED,
|
||||
touchX - newWin.mFrame.left, touchY - newWin.mFrame.top,
|
||||
null, desc, null, false);
|
||||
try {
|
||||
newWin.mClient.dispatchDragEvent(event);
|
||||
// track each window that we've notified that the drag is starting
|
||||
mNotifiedWindows.add(newWin);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(WindowManagerService.TAG, "Unable to drag-start window " + newWin);
|
||||
} finally {
|
||||
// if the callee was local, the dispatch has already recycled the event
|
||||
if (Process.myPid() != newWin.mSession.mPid) {
|
||||
event.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* helper - construct and send a DRAG_STARTED event only if the window has not
|
||||
* previously been notified, i.e. it became visible after the drag operation
|
||||
* was begun. This is a rare case.
|
||||
*/
|
||||
void sendDragStartedIfNeededLw(WindowState newWin) {
|
||||
if (mDragInProgress) {
|
||||
// If we have sent the drag-started, we needn't do so again
|
||||
for (WindowState ws : mNotifiedWindows) {
|
||||
if (ws == newWin) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "need to send DRAG_STARTED to new window " + newWin);
|
||||
}
|
||||
sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
|
||||
}
|
||||
}
|
||||
|
||||
void broadcastDragEndedLw() {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
|
||||
}
|
||||
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
|
||||
0, 0, null, null, null, mDragResult);
|
||||
for (WindowState ws: mNotifiedWindows) {
|
||||
try {
|
||||
ws.mClient.dispatchDragEvent(evt);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
|
||||
}
|
||||
}
|
||||
mNotifiedWindows.clear();
|
||||
mDragInProgress = false;
|
||||
evt.recycle();
|
||||
}
|
||||
|
||||
void endDragLw() {
|
||||
mService.mDragState.broadcastDragEndedLw();
|
||||
|
||||
// stop intercepting input
|
||||
mService.mDragState.unregister();
|
||||
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
|
||||
|
||||
// free our resources and drop all the object references
|
||||
mService.mDragState.reset();
|
||||
mService.mDragState = null;
|
||||
|
||||
if (WindowManagerService.DEBUG_ORIENTATION) Slog.d(WindowManagerService.TAG, "Performing post-drag rotation");
|
||||
boolean changed = mService.setRotationUncheckedLocked(
|
||||
WindowManagerPolicy.USE_LAST_ROTATION, 0, false);
|
||||
if (changed) {
|
||||
mService.mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyMoveLw(float x, float y) {
|
||||
final int myPid = Process.myPid();
|
||||
|
||||
// Move the surface to the given touch
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
|
||||
Surface.openTransaction();
|
||||
try {
|
||||
mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DRAG "
|
||||
+ mSurface + ": pos=(" +
|
||||
(int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
|
||||
} finally {
|
||||
Surface.closeTransaction();
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
|
||||
}
|
||||
|
||||
// Tell the affected window
|
||||
WindowState touchedWin = getTouchedWinAtPointLw(x, y);
|
||||
if (touchedWin == null) {
|
||||
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "No touched win at x=" + x + " y=" + y);
|
||||
return;
|
||||
}
|
||||
if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
|
||||
final IBinder touchedBinder = touchedWin.mClient.asBinder();
|
||||
if (touchedBinder != mLocalWin) {
|
||||
// This drag is pinned only to the originating window, but the drag
|
||||
// point is outside that window. Pretend it's over empty space.
|
||||
touchedWin = null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
// have we dragged over a new window?
|
||||
if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow);
|
||||
}
|
||||
// force DRAG_EXITED_EVENT if appropriate
|
||||
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
|
||||
x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top,
|
||||
null, null, null, false);
|
||||
mTargetWindow.mClient.dispatchDragEvent(evt);
|
||||
if (myPid != mTargetWindow.mSession.mPid) {
|
||||
evt.recycle();
|
||||
}
|
||||
}
|
||||
if (touchedWin != null) {
|
||||
if (false && WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
|
||||
}
|
||||
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
|
||||
x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
|
||||
null, null, null, false);
|
||||
touchedWin.mClient.dispatchDragEvent(evt);
|
||||
if (myPid != touchedWin.mSession.mPid) {
|
||||
evt.recycle();
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(WindowManagerService.TAG, "can't send drag notification to windows");
|
||||
}
|
||||
mTargetWindow = touchedWin;
|
||||
}
|
||||
|
||||
// Tell the drop target about the data. Returns 'true' if we can immediately
|
||||
// dispatch the global drag-ended message, 'false' if we need to wait for a
|
||||
// result from the recipient.
|
||||
boolean notifyDropLw(float x, float y) {
|
||||
WindowState touchedWin = getTouchedWinAtPointLw(x, y);
|
||||
if (touchedWin == null) {
|
||||
// "drop" outside a valid window -- no recipient to apply a
|
||||
// timeout to, and we can send the drag-ended message immediately.
|
||||
mDragResult = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "sending DROP to " + touchedWin);
|
||||
}
|
||||
final int myPid = Process.myPid();
|
||||
final IBinder token = touchedWin.mClient.asBinder();
|
||||
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP,
|
||||
x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
|
||||
null, null, mData, false);
|
||||
try {
|
||||
touchedWin.mClient.dispatchDragEvent(evt);
|
||||
|
||||
// 5 second timeout for this window to respond to the drop
|
||||
mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
|
||||
Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
|
||||
mService.mH.sendMessageDelayed(msg, 5000);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(WindowManagerService.TAG, "can't send drop notification to win " + touchedWin);
|
||||
return true;
|
||||
} finally {
|
||||
if (myPid != touchedWin.mSession.mPid) {
|
||||
evt.recycle();
|
||||
}
|
||||
}
|
||||
mToken = token;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the visible, touch-deliverable window under the given point
|
||||
private WindowState getTouchedWinAtPointLw(float xf, float yf) {
|
||||
WindowState touchedWin = null;
|
||||
final int x = (int) xf;
|
||||
final int y = (int) yf;
|
||||
final ArrayList<WindowState> windows = mService.mWindows;
|
||||
final int N = windows.size();
|
||||
for (int i = N - 1; i >= 0; i--) {
|
||||
WindowState child = windows.get(i);
|
||||
final int flags = child.mAttrs.flags;
|
||||
if (!child.isVisibleLw()) {
|
||||
// not visible == don't tell about drags
|
||||
continue;
|
||||
}
|
||||
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
|
||||
// not touchable == don't tell about drags
|
||||
continue;
|
||||
}
|
||||
|
||||
child.getTouchableRegion(mTmpRegion);
|
||||
|
||||
final int touchFlags = flags &
|
||||
(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
|
||||
if (mTmpRegion.contains(x, y) || touchFlags == 0) {
|
||||
// Found it
|
||||
touchedWin = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return touchedWin;
|
||||
}
|
||||
}
|
||||
44
services/java/com/android/server/wm/FadeInOutAnimation.java
Normal file
44
services/java/com/android/server/wm/FadeInOutAnimation.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
/**
|
||||
* Animation that fade in after 0.5 interpolate time, or fade out in reverse order.
|
||||
* This is used for opening/closing transition for apps in compatible mode.
|
||||
*/
|
||||
class FadeInOutAnimation extends Animation {
|
||||
boolean mFadeIn;
|
||||
|
||||
public FadeInOutAnimation(boolean fadeIn) {
|
||||
setInterpolator(new AccelerateInterpolator());
|
||||
setDuration(WindowManagerService.DEFAULT_FADE_IN_OUT_DURATION);
|
||||
mFadeIn = fadeIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
float x = interpolatedTime;
|
||||
if (!mFadeIn) {
|
||||
x = 1.0f - x; // reverse the interpolation for fade out
|
||||
}
|
||||
t.setAlpha(x);
|
||||
}
|
||||
}
|
||||
@@ -30,11 +30,11 @@ public final class InputApplicationHandle {
|
||||
private int ptr;
|
||||
|
||||
// The window manager's application window token.
|
||||
public final WindowManagerService.AppWindowToken appWindowToken;
|
||||
public final AppWindowToken appWindowToken;
|
||||
|
||||
private native void nativeDispose();
|
||||
|
||||
public InputApplicationHandle(WindowManagerService.AppWindowToken appWindowToken) {
|
||||
public InputApplicationHandle(AppWindowToken appWindowToken) {
|
||||
this.appWindowToken = appWindowToken;
|
||||
}
|
||||
|
||||
|
||||
364
services/java/com/android/server/wm/InputMonitor.java
Normal file
364
services/java/com/android/server/wm/InputMonitor.java
Normal file
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
final class InputMonitor {
|
||||
private final WindowManagerService mService;
|
||||
|
||||
// Current window with input focus for keys and other non-touch events. May be null.
|
||||
private WindowState mInputFocus;
|
||||
|
||||
// When true, prevents input dispatch from proceeding until set to false again.
|
||||
private boolean mInputDispatchFrozen;
|
||||
|
||||
// When true, input dispatch proceeds normally. Otherwise all events are dropped.
|
||||
private boolean mInputDispatchEnabled = true;
|
||||
|
||||
// When true, need to call updateInputWindowsLw().
|
||||
private boolean mUpdateInputWindowsNeeded = true;
|
||||
|
||||
// Temporary list of windows information to provide to the input dispatcher.
|
||||
private InputWindowList mTempInputWindows = new InputWindowList();
|
||||
|
||||
// Temporary input application object to provide to the input dispatcher.
|
||||
private InputApplication mTempInputApplication = new InputApplication();
|
||||
|
||||
// Set to true when the first input device configuration change notification
|
||||
// is received to indicate that the input devices are ready.
|
||||
private final Object mInputDevicesReadyMonitor = new Object();
|
||||
private boolean mInputDevicesReady;
|
||||
|
||||
public InputMonitor(WindowManagerService service) {
|
||||
mService = service;
|
||||
}
|
||||
|
||||
/* Notifies the window manager about a broken input channel.
|
||||
*
|
||||
* Called by the InputManager.
|
||||
*/
|
||||
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
|
||||
if (inputWindowHandle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mService.mWindowMap) {
|
||||
WindowState windowState = (WindowState) inputWindowHandle.windowState;
|
||||
Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
|
||||
mService.removeWindowLocked(windowState.mSession, windowState);
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifies the window manager about an application that is not responding.
|
||||
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
|
||||
*
|
||||
* Called by the InputManager.
|
||||
*/
|
||||
public long notifyANR(InputApplicationHandle inputApplicationHandle,
|
||||
InputWindowHandle inputWindowHandle) {
|
||||
AppWindowToken appWindowToken = null;
|
||||
if (inputWindowHandle != null) {
|
||||
synchronized (mService.mWindowMap) {
|
||||
WindowState windowState = (WindowState) inputWindowHandle.windowState;
|
||||
if (windowState != null) {
|
||||
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to "
|
||||
+ windowState.mAttrs.getTitle());
|
||||
appWindowToken = windowState.mAppToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appWindowToken == null && inputApplicationHandle != null) {
|
||||
appWindowToken = inputApplicationHandle.appWindowToken;
|
||||
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application "
|
||||
+ appWindowToken.stringName);
|
||||
}
|
||||
|
||||
if (appWindowToken != null && appWindowToken.appToken != null) {
|
||||
try {
|
||||
// Notify the activity manager about the timeout and let it decide whether
|
||||
// to abort dispatching or keep waiting.
|
||||
boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
|
||||
if (! abort) {
|
||||
// The activity manager declined to abort dispatching.
|
||||
// Wait a bit longer and timeout again later.
|
||||
return appWindowToken.inputDispatchingTimeoutNanos;
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
return 0; // abort dispatching
|
||||
}
|
||||
|
||||
private void addDragInputWindowLw(InputWindowList windowList) {
|
||||
final InputWindow inputWindow = windowList.add();
|
||||
inputWindow.inputChannel = mService.mDragState.mServerChannel;
|
||||
inputWindow.name = "drag";
|
||||
inputWindow.layoutParamsFlags = 0;
|
||||
inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
|
||||
inputWindow.dispatchingTimeoutNanos = WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
|
||||
inputWindow.visible = true;
|
||||
inputWindow.canReceiveKeys = false;
|
||||
inputWindow.hasFocus = true;
|
||||
inputWindow.hasWallpaper = false;
|
||||
inputWindow.paused = false;
|
||||
inputWindow.layer = mService.mDragState.getDragLayerLw();
|
||||
inputWindow.ownerPid = Process.myPid();
|
||||
inputWindow.ownerUid = Process.myUid();
|
||||
|
||||
// The drag window covers the entire display
|
||||
inputWindow.frameLeft = 0;
|
||||
inputWindow.frameTop = 0;
|
||||
inputWindow.frameRight = mService.mDisplay.getWidth();
|
||||
inputWindow.frameBottom = mService.mDisplay.getHeight();
|
||||
|
||||
// The drag window cannot receive new touches.
|
||||
inputWindow.touchableRegion.setEmpty();
|
||||
}
|
||||
|
||||
public void setUpdateInputWindowsNeededLw() {
|
||||
mUpdateInputWindowsNeeded = true;
|
||||
}
|
||||
|
||||
/* Updates the cached window information provided to the input dispatcher. */
|
||||
public void updateInputWindowsLw(boolean force) {
|
||||
if (!force && !mUpdateInputWindowsNeeded) {
|
||||
return;
|
||||
}
|
||||
mUpdateInputWindowsNeeded = false;
|
||||
|
||||
// Populate the input window list with information about all of the windows that
|
||||
// could potentially receive input.
|
||||
// As an optimization, we could try to prune the list of windows but this turns
|
||||
// out to be difficult because only the native code knows for sure which window
|
||||
// currently has touch focus.
|
||||
final ArrayList<WindowState> windows = mService.mWindows;
|
||||
|
||||
// If there's a drag in flight, provide a pseudowindow to catch drag input
|
||||
final boolean inDrag = (mService.mDragState != null);
|
||||
if (inDrag) {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Log.d(WindowManagerService.TAG, "Inserting drag window");
|
||||
}
|
||||
addDragInputWindowLw(mTempInputWindows);
|
||||
}
|
||||
|
||||
final int N = windows.size();
|
||||
for (int i = N - 1; i >= 0; i--) {
|
||||
final WindowState child = windows.get(i);
|
||||
if (child.mInputChannel == null || child.mRemoved) {
|
||||
// Skip this window because it cannot possibly receive input.
|
||||
continue;
|
||||
}
|
||||
|
||||
final int flags = child.mAttrs.flags;
|
||||
final int type = child.mAttrs.type;
|
||||
|
||||
final boolean hasFocus = (child == mInputFocus);
|
||||
final boolean isVisible = child.isVisibleLw();
|
||||
final boolean hasWallpaper = (child == mService.mWallpaperTarget)
|
||||
&& (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
|
||||
|
||||
// If there's a drag in progress and 'child' is a potential drop target,
|
||||
// make sure it's been told about the drag
|
||||
if (inDrag && isVisible) {
|
||||
mService.mDragState.sendDragStartedIfNeededLw(child);
|
||||
}
|
||||
|
||||
// Add a window to our list of input windows.
|
||||
final InputWindow inputWindow = mTempInputWindows.add();
|
||||
inputWindow.inputWindowHandle = child.mInputWindowHandle;
|
||||
inputWindow.inputChannel = child.mInputChannel;
|
||||
inputWindow.name = child.toString();
|
||||
inputWindow.layoutParamsFlags = flags;
|
||||
inputWindow.layoutParamsType = type;
|
||||
inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
|
||||
inputWindow.visible = isVisible;
|
||||
inputWindow.canReceiveKeys = child.canReceiveKeys();
|
||||
inputWindow.hasFocus = hasFocus;
|
||||
inputWindow.hasWallpaper = hasWallpaper;
|
||||
inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
|
||||
inputWindow.layer = child.mLayer;
|
||||
inputWindow.ownerPid = child.mSession.mPid;
|
||||
inputWindow.ownerUid = child.mSession.mUid;
|
||||
|
||||
final Rect frame = child.mFrame;
|
||||
inputWindow.frameLeft = frame.left;
|
||||
inputWindow.frameTop = frame.top;
|
||||
inputWindow.frameRight = frame.right;
|
||||
inputWindow.frameBottom = frame.bottom;
|
||||
|
||||
child.getTouchableRegion(inputWindow.touchableRegion);
|
||||
}
|
||||
|
||||
// Send windows to native code.
|
||||
mService.mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
|
||||
|
||||
// Clear the list in preparation for the next round.
|
||||
// Also avoids keeping InputChannel objects referenced unnecessarily.
|
||||
mTempInputWindows.clear();
|
||||
}
|
||||
|
||||
/* Notifies that the input device configuration has changed. */
|
||||
public void notifyConfigurationChanged() {
|
||||
mService.sendNewConfiguration();
|
||||
|
||||
synchronized (mInputDevicesReadyMonitor) {
|
||||
if (!mInputDevicesReady) {
|
||||
mInputDevicesReady = true;
|
||||
mInputDevicesReadyMonitor.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Waits until the built-in input devices have been configured. */
|
||||
public boolean waitForInputDevicesReady(long timeoutMillis) {
|
||||
synchronized (mInputDevicesReadyMonitor) {
|
||||
if (!mInputDevicesReady) {
|
||||
try {
|
||||
mInputDevicesReadyMonitor.wait(timeoutMillis);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
return mInputDevicesReady;
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifies that the lid switch changed state. */
|
||||
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
|
||||
mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
|
||||
}
|
||||
|
||||
/* Provides an opportunity for the window manager policy to intercept early key
|
||||
* processing as soon as the key has been read from the device. */
|
||||
public int interceptKeyBeforeQueueing(
|
||||
KeyEvent event, int policyFlags, boolean isScreenOn) {
|
||||
return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
|
||||
}
|
||||
|
||||
/* Provides an opportunity for the window manager policy to process a key before
|
||||
* ordinary dispatch. */
|
||||
public boolean interceptKeyBeforeDispatching(
|
||||
InputWindowHandle focus, KeyEvent event, int policyFlags) {
|
||||
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
|
||||
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
|
||||
}
|
||||
|
||||
/* Provides an opportunity for the window manager policy to process a key that
|
||||
* the application did not handle. */
|
||||
public KeyEvent dispatchUnhandledKey(
|
||||
InputWindowHandle focus, KeyEvent event, int policyFlags) {
|
||||
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
|
||||
return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
|
||||
}
|
||||
|
||||
/* Called when the current input focus changes.
|
||||
* Layer assignment is assumed to be complete by the time this is called.
|
||||
*/
|
||||
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
|
||||
if (WindowManagerService.DEBUG_INPUT) {
|
||||
Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
|
||||
}
|
||||
|
||||
if (newWindow != mInputFocus) {
|
||||
if (newWindow != null && newWindow.canReceiveKeys()) {
|
||||
// Displaying a window implicitly causes dispatching to be unpaused.
|
||||
// This is to protect against bugs if someone pauses dispatching but
|
||||
// forgets to resume.
|
||||
newWindow.mToken.paused = false;
|
||||
}
|
||||
|
||||
mInputFocus = newWindow;
|
||||
setUpdateInputWindowsNeededLw();
|
||||
|
||||
if (updateInputWindows) {
|
||||
updateInputWindowsLw(false /*force*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setFocusedAppLw(AppWindowToken newApp) {
|
||||
// Focused app has changed.
|
||||
if (newApp == null) {
|
||||
mService.mInputManager.setFocusedApplication(null);
|
||||
} else {
|
||||
mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle;
|
||||
mTempInputApplication.name = newApp.toString();
|
||||
mTempInputApplication.dispatchingTimeoutNanos =
|
||||
newApp.inputDispatchingTimeoutNanos;
|
||||
|
||||
mService.mInputManager.setFocusedApplication(mTempInputApplication);
|
||||
|
||||
mTempInputApplication.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseDispatchingLw(WindowToken window) {
|
||||
if (! window.paused) {
|
||||
if (WindowManagerService.DEBUG_INPUT) {
|
||||
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
|
||||
}
|
||||
|
||||
window.paused = true;
|
||||
updateInputWindowsLw(true /*force*/);
|
||||
}
|
||||
}
|
||||
|
||||
public void resumeDispatchingLw(WindowToken window) {
|
||||
if (window.paused) {
|
||||
if (WindowManagerService.DEBUG_INPUT) {
|
||||
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
|
||||
}
|
||||
|
||||
window.paused = false;
|
||||
updateInputWindowsLw(true /*force*/);
|
||||
}
|
||||
}
|
||||
|
||||
public void freezeInputDispatchingLw() {
|
||||
if (! mInputDispatchFrozen) {
|
||||
if (WindowManagerService.DEBUG_INPUT) {
|
||||
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
|
||||
}
|
||||
|
||||
mInputDispatchFrozen = true;
|
||||
updateInputDispatchModeLw();
|
||||
}
|
||||
}
|
||||
|
||||
public void thawInputDispatchingLw() {
|
||||
if (mInputDispatchFrozen) {
|
||||
if (WindowManagerService.DEBUG_INPUT) {
|
||||
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
|
||||
}
|
||||
|
||||
mInputDispatchFrozen = false;
|
||||
updateInputDispatchModeLw();
|
||||
}
|
||||
}
|
||||
|
||||
public void setEventDispatchingLw(boolean enabled) {
|
||||
if (mInputDispatchEnabled != enabled) {
|
||||
if (WindowManagerService.DEBUG_INPUT) {
|
||||
Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
|
||||
}
|
||||
|
||||
mInputDispatchEnabled = enabled;
|
||||
updateInputDispatchModeLw();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInputDispatchModeLw() {
|
||||
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm; // TODO: use com.android.server.wm, once things move there
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
419
services/java/com/android/server/wm/Session.java
Normal file
419
services/java/com/android/server/wm/Session.java
Normal file
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import com.android.internal.view.IInputContext;
|
||||
import com.android.internal.view.IInputMethodClient;
|
||||
import com.android.internal.view.IInputMethodManager;
|
||||
import com.android.server.wm.WindowManagerService.H;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Slog;
|
||||
import android.view.IWindow;
|
||||
import android.view.IWindowSession;
|
||||
import android.view.InputChannel;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* This class represents an active client session. There is generally one
|
||||
* Session object per process that is interacting with the window manager.
|
||||
*/
|
||||
final class Session extends IWindowSession.Stub
|
||||
implements IBinder.DeathRecipient {
|
||||
final WindowManagerService mService;
|
||||
final IInputMethodClient mClient;
|
||||
final IInputContext mInputContext;
|
||||
final int mUid;
|
||||
final int mPid;
|
||||
final String mStringName;
|
||||
SurfaceSession mSurfaceSession;
|
||||
int mNumWindow = 0;
|
||||
boolean mClientDead = false;
|
||||
|
||||
public Session(WindowManagerService service, IInputMethodClient client,
|
||||
IInputContext inputContext) {
|
||||
mService = service;
|
||||
mClient = client;
|
||||
mInputContext = inputContext;
|
||||
mUid = Binder.getCallingUid();
|
||||
mPid = Binder.getCallingPid();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Session{");
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append(" uid ");
|
||||
sb.append(mUid);
|
||||
sb.append("}");
|
||||
mStringName = sb.toString();
|
||||
|
||||
synchronized (mService.mWindowMap) {
|
||||
if (mService.mInputMethodManager == null && mService.mHaveInputMethods) {
|
||||
IBinder b = ServiceManager.getService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b);
|
||||
}
|
||||
}
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// Note: it is safe to call in to the input method manager
|
||||
// here because we are not holding our lock.
|
||||
if (mService.mInputMethodManager != null) {
|
||||
mService.mInputMethodManager.addClient(client, inputContext,
|
||||
mUid, mPid);
|
||||
} else {
|
||||
client.setUsingInputMethod(false);
|
||||
}
|
||||
client.asBinder().linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
// The caller has died, so we can just forget about this.
|
||||
try {
|
||||
if (mService.mInputMethodManager != null) {
|
||||
mService.mInputMethodManager.removeClient(client);
|
||||
}
|
||||
} catch (RemoteException ee) {
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||
throws RemoteException {
|
||||
try {
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
} catch (RuntimeException e) {
|
||||
// Log all 'real' exceptions thrown to the caller
|
||||
if (!(e instanceof SecurityException)) {
|
||||
Slog.e(WindowManagerService.TAG, "Window Session Crash", e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
// Note: it is safe to call in to the input method manager
|
||||
// here because we are not holding our lock.
|
||||
try {
|
||||
if (mService.mInputMethodManager != null) {
|
||||
mService.mInputMethodManager.removeClient(mClient);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
synchronized(mService.mWindowMap) {
|
||||
mClient.asBinder().unlinkToDeath(this, 0);
|
||||
mClientDead = true;
|
||||
killSessionLocked();
|
||||
}
|
||||
}
|
||||
|
||||
public int add(IWindow window, WindowManager.LayoutParams attrs,
|
||||
int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
|
||||
return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets,
|
||||
outInputChannel);
|
||||
}
|
||||
|
||||
public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
|
||||
int viewVisibility, Rect outContentInsets) {
|
||||
return mService.addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
|
||||
}
|
||||
|
||||
public void remove(IWindow window) {
|
||||
mService.removeWindow(this, window);
|
||||
}
|
||||
|
||||
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
|
||||
int requestedWidth, int requestedHeight, int viewFlags,
|
||||
boolean insetsPending, Rect outFrame, Rect outContentInsets,
|
||||
Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
|
||||
//Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
|
||||
int res = mService.relayoutWindow(this, window, attrs,
|
||||
requestedWidth, requestedHeight, viewFlags, insetsPending,
|
||||
outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
|
||||
//Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
|
||||
return res;
|
||||
}
|
||||
|
||||
public void setTransparentRegion(IWindow window, Region region) {
|
||||
mService.setTransparentRegionWindow(this, window, region);
|
||||
}
|
||||
|
||||
public void setInsets(IWindow window, int touchableInsets,
|
||||
Rect contentInsets, Rect visibleInsets, Region touchableArea) {
|
||||
mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
|
||||
visibleInsets, touchableArea);
|
||||
}
|
||||
|
||||
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
|
||||
mService.getWindowDisplayFrame(this, window, outDisplayFrame);
|
||||
}
|
||||
|
||||
public void finishDrawing(IWindow window) {
|
||||
if (WindowManagerService.localLOGV) Slog.v(
|
||||
WindowManagerService.TAG, "IWindow finishDrawing called for " + window);
|
||||
mService.finishDrawingWindow(this, window);
|
||||
}
|
||||
|
||||
public void setInTouchMode(boolean mode) {
|
||||
synchronized(mService.mWindowMap) {
|
||||
mService.mInTouchMode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getInTouchMode() {
|
||||
synchronized(mService.mWindowMap) {
|
||||
return mService.mInTouchMode;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean performHapticFeedback(IWindow window, int effectId,
|
||||
boolean always) {
|
||||
synchronized(mService.mWindowMap) {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mService.mPolicy.performHapticFeedbackLw(
|
||||
mService.windowForClientLocked(this, window, true),
|
||||
effectId, always);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Drag/drop */
|
||||
public IBinder prepareDrag(IWindow window, int flags,
|
||||
int width, int height, Surface outSurface) {
|
||||
return mService.prepareDragSurface(window, mSurfaceSession, flags,
|
||||
width, height, outSurface);
|
||||
}
|
||||
|
||||
public boolean performDrag(IWindow window, IBinder dragToken,
|
||||
float touchX, float touchY, float thumbCenterX, float thumbCenterY,
|
||||
ClipData data) {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "perform drag: win=" + window + " data=" + data);
|
||||
}
|
||||
|
||||
synchronized (mService.mWindowMap) {
|
||||
if (mService.mDragState == null) {
|
||||
Slog.w(WindowManagerService.TAG, "No drag prepared");
|
||||
throw new IllegalStateException("performDrag() without prepareDrag()");
|
||||
}
|
||||
|
||||
if (dragToken != mService.mDragState.mToken) {
|
||||
Slog.w(WindowManagerService.TAG, "Performing mismatched drag");
|
||||
throw new IllegalStateException("performDrag() does not match prepareDrag()");
|
||||
}
|
||||
|
||||
WindowState callingWin = mService.windowForClientLocked(null, window, false);
|
||||
if (callingWin == null) {
|
||||
Slog.w(WindowManagerService.TAG, "Bad requesting window " + window);
|
||||
return false; // !!! TODO: throw here?
|
||||
}
|
||||
|
||||
// !!! TODO: if input is not still focused on the initiating window, fail
|
||||
// the drag initiation (e.g. an alarm window popped up just as the application
|
||||
// called performDrag()
|
||||
|
||||
mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
|
||||
|
||||
// !!! TODO: extract the current touch (x, y) in screen coordinates. That
|
||||
// will let us eliminate the (touchX,touchY) parameters from the API.
|
||||
|
||||
// !!! FIXME: put all this heavy stuff onto the mH looper, as well as
|
||||
// the actual drag event dispatch stuff in the dragstate
|
||||
|
||||
mService.mDragState.register();
|
||||
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
|
||||
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
|
||||
mService.mDragState.mServerChannel)) {
|
||||
Slog.e(WindowManagerService.TAG, "Unable to transfer touch focus");
|
||||
mService.mDragState.unregister();
|
||||
mService.mDragState = null;
|
||||
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
|
||||
return false;
|
||||
}
|
||||
|
||||
mService.mDragState.mData = data;
|
||||
mService.mDragState.mCurrentX = touchX;
|
||||
mService.mDragState.mCurrentY = touchY;
|
||||
mService.mDragState.broadcastDragStartedLw(touchX, touchY);
|
||||
|
||||
// remember the thumb offsets for later
|
||||
mService.mDragState.mThumbOffsetX = thumbCenterX;
|
||||
mService.mDragState.mThumbOffsetY = thumbCenterY;
|
||||
|
||||
// Make the surface visible at the proper location
|
||||
final Surface surface = mService.mDragState.mSurface;
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
|
||||
Surface.openTransaction();
|
||||
try {
|
||||
surface.setPosition((int)(touchX - thumbCenterX),
|
||||
(int)(touchY - thumbCenterY));
|
||||
surface.setAlpha(.7071f);
|
||||
surface.setLayer(mService.mDragState.getDragLayerLw());
|
||||
surface.show();
|
||||
} finally {
|
||||
Surface.closeTransaction();
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION performDrag");
|
||||
}
|
||||
}
|
||||
|
||||
return true; // success!
|
||||
}
|
||||
|
||||
public void reportDropResult(IWindow window, boolean consumed) {
|
||||
IBinder token = window.asBinder();
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "Drop result=" + consumed + " reported by " + token);
|
||||
}
|
||||
|
||||
synchronized (mService.mWindowMap) {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (mService.mDragState == null || mService.mDragState.mToken != token) {
|
||||
Slog.w(WindowManagerService.TAG, "Invalid drop-result claim by " + window);
|
||||
throw new IllegalStateException("reportDropResult() by non-recipient");
|
||||
}
|
||||
|
||||
// The right window has responded, even if it's no longer around,
|
||||
// so be sure to halt the timeout even if the later WindowState
|
||||
// lookup fails.
|
||||
mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
|
||||
WindowState callingWin = mService.windowForClientLocked(null, window, false);
|
||||
if (callingWin == null) {
|
||||
Slog.w(WindowManagerService.TAG, "Bad result-reporting window " + window);
|
||||
return; // !!! TODO: throw here?
|
||||
}
|
||||
|
||||
mService.mDragState.mDragResult = consumed;
|
||||
mService.mDragState.endDragLw();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dragRecipientEntered(IWindow window) {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder());
|
||||
}
|
||||
}
|
||||
|
||||
public void dragRecipientExited(IWindow window) {
|
||||
if (WindowManagerService.DEBUG_DRAG) {
|
||||
Slog.d(WindowManagerService.TAG, "Drag from old candidate view @ " + window.asBinder());
|
||||
}
|
||||
}
|
||||
|
||||
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
|
||||
synchronized(mService.mWindowMap) {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mService.setWindowWallpaperPositionLocked(
|
||||
mService.windowForClientLocked(this, window, true),
|
||||
x, y, xStep, yStep);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void wallpaperOffsetsComplete(IBinder window) {
|
||||
mService.wallpaperOffsetsComplete(window);
|
||||
}
|
||||
|
||||
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
|
||||
int z, Bundle extras, boolean sync) {
|
||||
synchronized(mService.mWindowMap) {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mService.sendWindowWallpaperCommandLocked(
|
||||
mService.windowForClientLocked(this, window, true),
|
||||
action, x, y, z, extras, sync);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void wallpaperCommandComplete(IBinder window, Bundle result) {
|
||||
mService.wallpaperCommandComplete(window, result);
|
||||
}
|
||||
|
||||
void windowAddedLocked() {
|
||||
if (mSurfaceSession == null) {
|
||||
if (WindowManagerService.localLOGV) Slog.v(
|
||||
WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
|
||||
mSurfaceSession = new SurfaceSession();
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
|
||||
WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession);
|
||||
mService.mSessions.add(this);
|
||||
}
|
||||
mNumWindow++;
|
||||
}
|
||||
|
||||
void windowRemovedLocked() {
|
||||
mNumWindow--;
|
||||
killSessionLocked();
|
||||
}
|
||||
|
||||
void killSessionLocked() {
|
||||
if (mNumWindow <= 0 && mClientDead) {
|
||||
mService.mSessions.remove(this);
|
||||
if (mSurfaceSession != null) {
|
||||
if (WindowManagerService.localLOGV) Slog.v(
|
||||
WindowManagerService.TAG, "Last window removed from " + this
|
||||
+ ", destroying " + mSurfaceSession);
|
||||
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
|
||||
WindowManagerService.TAG, " KILL SURFACE SESSION " + mSurfaceSession);
|
||||
try {
|
||||
mSurfaceSession.kill();
|
||||
} catch (Exception e) {
|
||||
Slog.w(WindowManagerService.TAG, "Exception thrown when killing surface session "
|
||||
+ mSurfaceSession + " in session " + this
|
||||
+ ": " + e.toString());
|
||||
}
|
||||
mSurfaceSession = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
|
||||
pw.print(" mClientDead="); pw.print(mClientDead);
|
||||
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mStringName;
|
||||
}
|
||||
}
|
||||
36
services/java/com/android/server/wm/StartingData.java
Normal file
36
services/java/com/android/server/wm/StartingData.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
final class StartingData {
|
||||
final String pkg;
|
||||
final int theme;
|
||||
final CharSequence nonLocalizedLabel;
|
||||
final int labelRes;
|
||||
final int icon;
|
||||
final int windowFlags;
|
||||
|
||||
StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
|
||||
int _labelRes, int _icon, int _windowFlags) {
|
||||
pkg = _pkg;
|
||||
theme = _theme;
|
||||
nonLocalizedLabel = _nonLocalizedLabel;
|
||||
labelRes = _labelRes;
|
||||
icon = _icon;
|
||||
windowFlags = _windowFlags;
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm; // TODO: use com.android.server.wm, once things move there
|
||||
package com.android.server.wm;
|
||||
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
177
services/java/com/android/server/wm/Watermark.java
Normal file
177
services/java/com/android/server/wm/Watermark.java
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.Surface.OutOfResourcesException;
|
||||
|
||||
/**
|
||||
* Displays a watermark on top of the window manager's windows.
|
||||
*/
|
||||
class Watermark {
|
||||
final String[] mTokens;
|
||||
final String mText;
|
||||
final Paint mTextPaint;
|
||||
final int mTextWidth;
|
||||
final int mTextHeight;
|
||||
final int mTextAscent;
|
||||
final int mTextDescent;
|
||||
final int mDeltaX;
|
||||
final int mDeltaY;
|
||||
|
||||
Surface mSurface;
|
||||
int mLastDW;
|
||||
int mLastDH;
|
||||
boolean mDrawNeeded;
|
||||
|
||||
Watermark(Display display, SurfaceSession session, String[] tokens) {
|
||||
final DisplayMetrics dm = new DisplayMetrics();
|
||||
display.getMetrics(dm);
|
||||
|
||||
if (false) {
|
||||
Log.i(WindowManagerService.TAG, "*********************** WATERMARK");
|
||||
for (int i=0; i<tokens.length; i++) {
|
||||
Log.i(WindowManagerService.TAG, " TOKEN #" + i + ": " + tokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mTokens = tokens;
|
||||
|
||||
StringBuilder builder = new StringBuilder(32);
|
||||
int len = mTokens[0].length();
|
||||
len = len & ~1;
|
||||
for (int i=0; i<len; i+=2) {
|
||||
int c1 = mTokens[0].charAt(i);
|
||||
int c2 = mTokens[0].charAt(i+1);
|
||||
if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10;
|
||||
else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10;
|
||||
else c1 -= '0';
|
||||
if (c2 >= 'a' && c2 <= 'f') c2 = c2 - 'a' + 10;
|
||||
else if (c2 >= 'A' && c2 <= 'F') c2 = c2 - 'A' + 10;
|
||||
else c2 -= '0';
|
||||
builder.append((char)(255-((c1*16)+c2)));
|
||||
}
|
||||
mText = builder.toString();
|
||||
if (false) {
|
||||
Log.i(WindowManagerService.TAG, "Final text: " + mText);
|
||||
}
|
||||
|
||||
int fontSize = WindowManagerService.getPropertyInt(tokens, 1,
|
||||
TypedValue.COMPLEX_UNIT_DIP, 20, dm);
|
||||
|
||||
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mTextPaint.setTextSize(fontSize);
|
||||
mTextPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
|
||||
|
||||
FontMetricsInt fm = mTextPaint.getFontMetricsInt();
|
||||
mTextWidth = (int)mTextPaint.measureText(mText);
|
||||
mTextAscent = fm.ascent;
|
||||
mTextDescent = fm.descent;
|
||||
mTextHeight = fm.descent - fm.ascent;
|
||||
|
||||
mDeltaX = WindowManagerService.getPropertyInt(tokens, 2,
|
||||
TypedValue.COMPLEX_UNIT_PX, mTextWidth*2, dm);
|
||||
mDeltaY = WindowManagerService.getPropertyInt(tokens, 3,
|
||||
TypedValue.COMPLEX_UNIT_PX, mTextHeight*3, dm);
|
||||
int shadowColor = WindowManagerService.getPropertyInt(tokens, 4,
|
||||
TypedValue.COMPLEX_UNIT_PX, 0xb0000000, dm);
|
||||
int color = WindowManagerService.getPropertyInt(tokens, 5,
|
||||
TypedValue.COMPLEX_UNIT_PX, 0x60ffffff, dm);
|
||||
int shadowRadius = WindowManagerService.getPropertyInt(tokens, 6,
|
||||
TypedValue.COMPLEX_UNIT_PX, 7, dm);
|
||||
int shadowDx = WindowManagerService.getPropertyInt(tokens, 8,
|
||||
TypedValue.COMPLEX_UNIT_PX, 0, dm);
|
||||
int shadowDy = WindowManagerService.getPropertyInt(tokens, 9,
|
||||
TypedValue.COMPLEX_UNIT_PX, 0, dm);
|
||||
|
||||
mTextPaint.setColor(color);
|
||||
mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
|
||||
|
||||
try {
|
||||
mSurface = new Surface(session, 0,
|
||||
"WatermarkSurface", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
|
||||
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
|
||||
mSurface.setPosition(0, 0);
|
||||
mSurface.show();
|
||||
} catch (OutOfResourcesException e) {
|
||||
}
|
||||
}
|
||||
|
||||
void positionSurface(int dw, int dh) {
|
||||
if (mLastDW != dw || mLastDH != dh) {
|
||||
mLastDW = dw;
|
||||
mLastDH = dh;
|
||||
mSurface.setSize(dw, dh);
|
||||
mDrawNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void drawIfNeeded() {
|
||||
if (mDrawNeeded) {
|
||||
final int dw = mLastDW;
|
||||
final int dh = mLastDH;
|
||||
|
||||
mDrawNeeded = false;
|
||||
Rect dirty = new Rect(0, 0, dw, dh);
|
||||
Canvas c = null;
|
||||
try {
|
||||
c = mSurface.lockCanvas(dirty);
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (OutOfResourcesException e) {
|
||||
}
|
||||
if (c != null) {
|
||||
c.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
|
||||
int deltaX = mDeltaX;
|
||||
int deltaY = mDeltaY;
|
||||
|
||||
// deltaX shouldn't be close to a round fraction of our
|
||||
// x step, or else things will line up too much.
|
||||
int div = (dw+mTextWidth)/deltaX;
|
||||
int rem = (dw+mTextWidth) - (div*deltaX);
|
||||
int qdelta = deltaX/4;
|
||||
if (rem < qdelta || rem > (deltaX-qdelta)) {
|
||||
deltaX += deltaX/3;
|
||||
}
|
||||
|
||||
int y = -mTextHeight;
|
||||
int x = -mTextWidth;
|
||||
while (y < (dh+mTextHeight)) {
|
||||
c.drawText(mText, x, y, mTextPaint);
|
||||
x += deltaX;
|
||||
if (x >= dw) {
|
||||
x -= (dw+mTextWidth);
|
||||
y += deltaY;
|
||||
}
|
||||
}
|
||||
mSurface.unlockCanvasAndPost(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1623
services/java/com/android/server/wm/WindowState.java
Normal file
1623
services/java/com/android/server/wm/WindowState.java
Normal file
File diff suppressed because it is too large
Load Diff
110
services/java/com/android/server/wm/WindowToken.java
Normal file
110
services/java/com/android/server/wm/WindowToken.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Container of a set of related windows in the window manager. Often this
|
||||
* is an AppWindowToken, which is the handle for an Activity that it uses
|
||||
* to display windows. For nested windows, there is a WindowToken created for
|
||||
* the parent window to manage its children.
|
||||
*/
|
||||
class WindowToken {
|
||||
// The window manager!
|
||||
final WindowManagerService service;
|
||||
|
||||
// The actual token.
|
||||
final IBinder token;
|
||||
|
||||
// The type of window this token is for, as per WindowManager.LayoutParams.
|
||||
final int windowType;
|
||||
|
||||
// Set if this token was explicitly added by a client, so should
|
||||
// not be removed when all windows are removed.
|
||||
final boolean explicit;
|
||||
|
||||
// For printing.
|
||||
String stringName;
|
||||
|
||||
// If this is an AppWindowToken, this is non-null.
|
||||
AppWindowToken appWindowToken;
|
||||
|
||||
// All of the windows associated with this token.
|
||||
final ArrayList<WindowState> windows = new ArrayList<WindowState>();
|
||||
|
||||
// Is key dispatching paused for this token?
|
||||
boolean paused = false;
|
||||
|
||||
// Should this token's windows be hidden?
|
||||
boolean hidden;
|
||||
|
||||
// Temporary for finding which tokens no longer have visible windows.
|
||||
boolean hasVisible;
|
||||
|
||||
// Set to true when this token is in a pending transaction where it
|
||||
// will be shown.
|
||||
boolean waitingToShow;
|
||||
|
||||
// Set to true when this token is in a pending transaction where it
|
||||
// will be hidden.
|
||||
boolean waitingToHide;
|
||||
|
||||
// Set to true when this token is in a pending transaction where its
|
||||
// windows will be put to the bottom of the list.
|
||||
boolean sendingToBottom;
|
||||
|
||||
// Set to true when this token is in a pending transaction where its
|
||||
// windows will be put to the top of the list.
|
||||
boolean sendingToTop;
|
||||
|
||||
WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) {
|
||||
service = _service;
|
||||
token = _token;
|
||||
windowType = type;
|
||||
explicit = _explicit;
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("token="); pw.println(token);
|
||||
pw.print(prefix); pw.print("windows="); pw.println(windows);
|
||||
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
|
||||
pw.print(" hidden="); pw.print(hidden);
|
||||
pw.print(" hasVisible="); pw.println(hasVisible);
|
||||
if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) {
|
||||
pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
|
||||
pw.print(" waitingToHide="); pw.print(waitingToHide);
|
||||
pw.print(" sendingToBottom="); pw.print(sendingToBottom);
|
||||
pw.print(" sendingToTop="); pw.println(sendingToTop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (stringName == null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("WindowToken{");
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append(" token="); sb.append(token); sb.append('}');
|
||||
stringName = sb.toString();
|
||||
}
|
||||
return stringName;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user