/*
* Copyright (C) 2006 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 android.view;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.View.MeasureSpec;
import android.view.Window.DecorView;
/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* detail of {@link Window}.
*
*/
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks {
private static final boolean DBG = false;
private static final String TAG = "ViewRoot";
static long sInstanceCount = 0;
static boolean mInitialized = false;
static RunQueue sRunQueues = new RunQueue();
static WindowSession sWindowSession;
final int[] mTmpLocation = new int[2];
final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
int mPendingEventSeq = 0;
private static long sDrawTime;
private static boolean DBG_FPS = false;
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
final W mWindow;
View mView;
View mFocusedView;
View mRealFocusedView; // this is not set to null in touch mode
int mViewVisibility;
boolean mAppVisible = true;
boolean mIsCreating;
boolean mDrawingAllowed;
int mWidth;
int mHeight;
boolean mIsAnimating;
final View.AttachInfo mAttachInfo;
InputChannel mInputChannel;
InputQueue.Callback mInputQueueCallback;
InputQueue mInputQueue;
boolean mTraversalScheduled;
boolean mWillDrawSoon;
boolean mLayoutRequested;
boolean mFirst;
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
boolean mWindowAttributesChanged = false;
boolean mAdded;
boolean mAddedTouchMode;
Rect mWinFrame; // frame given by window manager.
/* package */int mAddNesting;
boolean mScrollMayChange;
int mSoftInputMode;
View mLastScrolledFocus;
int mScrollY;
int mCurScrollY;
boolean mUseGL;
boolean mGlWanted;
private boolean mAttached = false;
final ViewConfiguration mViewConfiguration;
String mViewRootID;
public static int mDialogCount = 0;
public String getViewRootID() {
return this.mViewRootID;
}
private String getCanvasSuffix() {
return "_AppCanvas";
}
public String getCanvasId() {
return getViewRootID() + getCanvasSuffix();
}
Rect mTempRect; // used in the transaction to not thrash the heap.
Rect mVisRect; // used to retrieve visible rect of focused view.
private static int viewRootID = 0;
private static int getAViewRootID() {
return viewRootID++;
}
private static ArrayList viewRootList = new ArrayList();
private final Canvas mCanvas;
private Rect mDirty;
public static WindowSession getWindowSession() {
if (sWindowSession == null) {
sWindowSession = new WindowSession();
}
return sWindowSession;
}
public ViewRoot(Context context) {
mWidth = -1;
mHeight = -1;
mFirst = true; // true for the first time the view is added
mAdded = false;
int w = 480, h = 800;
/**
* @j2sNative
*
* w = window.innerWidth;
* h = window.innerHeight;
*/{}
mWinFrame = new Rect(0, 0, w, h);
getWindowSession();
mWindow = new W(this, context);
mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
mViewConfiguration = ViewConfiguration.get(context);
mDirty = new Rect();
mViewRootID = "ViewRoot_" + ViewRoot.getAViewRootID();
mTempRect = new Rect();
mVisRect = new Rect();
/**
@j2sNative
var rootView = document.getElementById(this.mViewRootID);
if (rootView != null)
console.log("rootView != null ??!! rootViewID: " + this.mViewRootID);
rootView = document.createElement("span");
rootView.style.position = "absolute";
rootView.style.display = "block";
rootView.style.visibility = "hidden";
rootView.id = this.mViewRootID;
rootView.style.left = "0px";
rootView.style.top = "0px";
rootView.tabIndex = "-1";
// rootView.style["background-color"] = "silver";
document.body.appendChild(rootView);
// Canvas to draw on
var canvas = document.createElement("canvas");
canvas.id = this.mViewRootID + this.getCanvasSuffix();
canvas.style.position = "absolute";
canvas.getContext("2d").globalCompositeOperation = 'destination-atop';
canvas.getContext("2d").fillStyle = 'rgba(255,255,255,1)';
rootView.appendChild(canvas);
*/{}
attachHandlerToView(this.mViewRootID);
mCanvas = new Canvas(this.getCanvasId());
viewRootList.add(this);
/**
* @j2sNative
* // We'd like to disable the html scroll because MayLoon has its own scrollbars
* document.body.scroll = "no";
* document.body.style.overflow = 'hidden';
* document.height = window.innerHeight;
* // Disable text selection in web page
* document.onselectstart = function () {return false};
*/{}
}
public void attachHandlerToView(String uid) {
/**
@j2sNative
var elem = document.getElementById(uid);
if (elem == null) return;
*/
{
}
ViewRoot viewRoot = this;
Log.d(TAG, "Attaching handlers");
for (int i = 0; i < HTML5Event.eventType.length; i++) {
String eventTypeName = HTML5Event.eventType[i];
/**
@j2sNative
elem.addEventListener(eventTypeName, function(event){viewRoot.eventForwarder(event);}, true);
*/
{
}
}
}
/**
* simulated KeyDownEvent (keyCode = KEYCODE_BACK , eventType = keydown )
* In Activity's global ReturnButton and click it to simulated KEYCODE_BACK KEY
*/
public static void simulateBack() {
KeyEvent keyEvent = HTML5Event.toKeyEvent("keydown", KeyEvent.KEYCODE_BACK);
ViewRoot viewRoot = (ViewRoot) viewRootList.get(viewRootList.size() - 1);
viewRoot.deliverKeyEventToViewHierarchy(keyEvent, false);
}
public static void simulateMenu() {
KeyEvent keyEvent = HTML5Event.toKeyEvent("keydown", KeyEvent.KEYCODE_MENU);
ViewRoot viewRoot = (ViewRoot) viewRootList.get(viewRootList.size() - 1);
viewRoot.deliverKeyEventToViewHierarchy(keyEvent, false);
keyEvent = HTML5Event.toKeyEvent("keyup", KeyEvent.KEYCODE_MENU);
viewRoot.deliverKeyEventToViewHierarchy(keyEvent, false);
}
public static long getInstanceCount() {
return sInstanceCount;
}
/**
* Each ViewRoot has three children: one background canvas, one foreground
* canvas and a DecorView object which is the real container for the view
* hierachey. The problem is, according to HTML5 event model, all event will
* only be dispatched to the TopMostElement, which is the foreground canvas
* in our case, but we really want the DecorView to handle the event
* (globalEventHandler), so we need to forward the event to the DecorView.
* The solution is basically borrowed from
* http://www.vinylfox.com/forwarding-mouse-events-through-layers/
*/
public void eventForwarder(Object e) {
String eventType = null;
String divID = "";
/**
* @j2sNative
* spanID = e.target.id;
* eventType = e.type;
* var span = document.getElementById(spanID);
* if (span == null) {
* return;
* }
* //span.hidden = "hidden";
* if (android.view.HTML5Event.isMouseEvent(eventType)) {
* var targetElem = document.elementFromPoint(e.clientX, e.clientY);
* if (targetElem == null) return;
* }
* //span.hidden = null;
*/{}
int keyCode = -1;
if (HTML5Event.isKeyEvent(eventType)) {
// global key event
/**
* @j2sNative
* keyCode = e.keyCode ? e.keyCode : e.which;
*/{}
KeyEvent keyEvent = HTML5Event.toKeyEvent(eventType, keyCode);
ViewRoot viewRoot = (ViewRoot) viewRootList.get(viewRootList.size() - 1);
viewRoot.deliverKeyEventToViewHierarchy(keyEvent, false);
} else if (eventType =="focusout") {
// Mayloon Workaround : Fixed bug 848
// Calculator cannot scroll when the length of number is larger then editText
/**
* @j2sNative
* var thisText = e.target;
* if ((thisText.tagName == "INPUT") && (thisText.style.textAlign == "right")) {
* thisText.scrollLeft = thisText.scrollWidth - thisText.clientWidth
* + thisText.style.paddingRight.substr(0, thisText.style.paddingRight.length - 2)
* + thisText.style.paddingLeft.substr(0, thisText.style.paddingLeft.length - 2);
*}
*/{}
} else {
int x = 0, y = 0;
/**
* @j2sNative
* x = e.pageX; y = e.pageY;
*/{}
// viewRoot.
dispatchPointerEvent(e, eventType, x, y);
}
}
/**
* See if the key event means we should leave touch mode (and leave touch
* mode if so).
* @param event The key event.
* @return Whether this key event should be consumed (meaning the act of
* leaving touch mode alone is considered the event).
*/
private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
final int action = event.getAction();
if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_MULTIPLE) {
return false;
}
if ((event.getFlags()&KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
return false;
}
// only relevant if we are in touch mode
if (!mAttachInfo.mInTouchMode) {
return false;
}
// if something like an edit text has focus and the user is typing,
// leave touch mode
//
// note: the condition of not being a keyboard key is kind of a hacky
// approximation of whether we think the focused view will want the
// key; if we knew for sure whether the focused view would consume
// the event, that would be better.
// if (isKeyboardKey(event) && mView != null && mView.hasFocus()) {
// mFocusedView = mView.findFocus();
// if ((mFocusedView instanceof ViewGroup)
// && ((ViewGroup) mFocusedView).getDescendantFocusability() ==
// ViewGroup.FOCUS_AFTER_DESCENDANTS) {
// // something has focus, but is holding it weakly as a container
// return false;
// }
// if (ensureTouchMode(false)) {
// throw new IllegalStateException("should not have changed focus "
// + "when leaving touch mode while a view has focus.");
// }
// return false;
// }
if (isDirectional(event.getKeyCode())) {
// no view has focus, so we leave touch mode (and find something
// to give focus to). the event is consumed if we were able to
// find something to give focus to.
return ensureTouchMode(false);
}
return false;
}
/**
* @param keyCode The key code
* @return True if the key is directional.
*/
static boolean isDirectional(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
return true;
}
return false;
}
private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
try {
if (mView != null && mAdded && event != null) {
final int action = event.getAction();
boolean isDown = (action == KeyEvent.ACTION_DOWN);
if (checkForLeavingTouchModeAndConsume(event)) {
return;
}
boolean keyHandled = mView.dispatchKeyEvent(event);
if (!keyHandled && isDown) {
int direction = 0;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
direction = View.FOCUS_LEFT;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
direction = View.FOCUS_RIGHT;
break;
case KeyEvent.KEYCODE_DPAD_UP:
direction = View.FOCUS_UP;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
direction = View.FOCUS_DOWN;
break;
}
if (direction != 0) {
View focused = mView != null ? mView.findFocus() : null;
if (focused != null) {
View v = focused.focusSearch(direction);
boolean focusPassed = false;
if (v != null && v != focused) {
// do the math the get the interesting rect
// of previous focused into the coord system of
// newly focused view
focused.getFocusedRect(mTempRect);
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focused, mTempRect);
((ViewGroup) mView).offsetRectIntoDescendantCoords(
v, mTempRect);
}
focusPassed = v.requestFocus(direction, mTempRect);
}
if (!focusPassed) {
mView.dispatchUnhandledMove(focused, direction);
}
}
}
}
}
} finally {
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
}
}
public void dispatchPointerEvent(Object e, String eventType, int x, int y) {
/**
* @j2sNative
* e.stopPropagation();
*/{}
Activity cur = ((ActivityManager) Context.getSystemContext()
.getSystemService(Context.ACTIVITY_SERVICE)).mCurActivity;
if (cur == null) {
Log.e(TAG, "mCurActivity is null?!");
return;
}
// View rootView = cur.getWindow().getDecorView().getRootView();
//
// View thisView = rootView.findViewByElementId(viewElementId);
// if (null != thisView) {
// if (eventType.equals("click")) {
// while (thisView.performClick() == false) {
// if (thisView.getParent() != null
// && thisView.getParent() instanceof View)
// thisView = (View) thisView.getParent();
// else
// break;
// }
// } else if (eventType.equals("touchstart")) {
// int x = 0, y = 0;
// /**
// @j2sNative
// x = e.touches.item(0).clientX;
// y = e.touches.item(0).clientY;
// */
// {
// }
// MotionEvent event = MotionEvent.obtain(0, 0,
// android.view.MotionEvent.ACTION_DOWN, x, y, 0, 0, 0, 0,
// 0, 0, 0);
// while (thisView.onTouchEvent(event) == false) {
// if (thisView.getParent() != null
// && thisView.getParent() instanceof View)
// thisView = (View) thisView.getParent();
// else
// break;
// }
// } else if (eventType.equals("touchend")) {
// int x = 0, y = 0;
// /**
// @j2sNative
// x = e.changedTouches.item(0).clientX;
// y = e.changedTouches.item(0).clientY;
// */
// {
// }
// MotionEvent event = MotionEvent.obtain(0, 0,
// android.view.MotionEvent.ACTION_UP, x, y, 0, 0, 0, 0,
// 0, 0, 0);
// while (thisView.onTouchEvent(event) == false) {
// if (thisView.getParent() != null
// && thisView.getParent() instanceof View)
// thisView = (View) thisView.getParent();
// else
// break;
// }
// }
// }
int action;
if (eventType.equals("mousedown")) {
action = MotionEvent.ACTION_DOWN;
} else if (eventType.equals("mouseup")) {
action = MotionEvent.ACTION_UP;
} else if (eventType.equals("mouseleave") || eventType.equals("mouseout")) {
action = MotionEvent.ACTION_OUTSIDE;
} else if (eventType.equals("mouseenter") || eventType.equals("mouseover")) {
// ignore by now
return;
} else if (eventType.equals("mousemove")) {
action = MotionEvent.ACTION_MOVE;
} else {
return;
}
// Dispatch motion event staring from DecroView
if (mView != null) {
// enter touch mode on the down
boolean isDown = action == MotionEvent.ACTION_DOWN;
if (isDown) {
ensureTouchMode(true);
}
// Compensate the position first because our Window maybe not at (0, 0)
x = x - this.mAttachInfo.mWindowLeft;
y = y - this.mAttachInfo.mWindowTop;
long time = SystemClock.uptimeMillis();
mView.dispatchTouchEvent(MotionEvent.obtain(time, time, action, x, y, 0));
}
}
/**
* Indicates whether we are in touch mode. Calling this method triggers an
* IPC call and should be avoided whenever possible.
*
* @return True, if the device is in touch mode, false otherwise.
* @hide
*/
// FIXME: what this return value means??
static boolean isInTouchMode() {
WindowManagerImpl mWindowManager = (WindowManagerImpl)WindowManagerImpl.getDefault();
return mWindowManager.getInTouchMode();
}
public View getView() {
return mView;
}
public void requestLayout() {
mLayoutRequested = true;
scheduleTraversals();
}
public boolean isLayoutRequested() {
return mLayoutRequested;
}
public ViewParent getParent() {
return null;
}
public void bringChildToFront(View child) {
}
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
sendEmptyMessage(DO_TRAVERSAL);
}
}
public void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
removeMessages(DO_TRAVERSAL);
}
}
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) throws Exception {
if (!(view instanceof DecorView)) {
throw new IllegalArgumentException(
"view must be an instance of DecorView");
}
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
attrs = mWindowAttributes;
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
mAttachInfo.mApplicationScale = 1.0f;
mAdded = true;
WindowManagerImpl mWindowManager = (WindowManagerImpl)WindowManagerImpl.getDefault();
mAddedTouchMode = mWindowManager.getInTouchMode();
makeHostView(view);
requestLayout();
}
}
private void makeHostView(View view) {
view.assignParent(this,true);
if (!(view instanceof DecorView)) {
view.zIndex = 10;
/**
@j2sNative
var thisView = document.getElementById(view.getUIElementID());
if (null == thisView) {
thisView = document.createElement("span");
thisView.style.position = "absolute";
thisView.id = view.getUIElementID();
thisView.style.display = "block";
}
document.body.appendChild(thisView);
*/{}
return;
}
int decorViewID = view.getUIElementID();
int decorZIndex = view.getZIndex();
if(view.getWidth()!=0&&view.getHeight()!=0){
mWinFrame = new Rect(0,0,view.getWidth(),view.getHeight());
}
/**
@j2sNative
var rootView = document.getElementById(this.mViewRootID);
rootView.style.zIndex = decorZIndex;
// Canvas to draw on
rootView.childNodes[0].style.zIndex = decorZIndex - 1;
// attach decorView
var decorView = document.getElementById(decorViewID);
if (decorView == null) {
decorView = document.createElement("span");
decorView.style.position = "absolute";
decorView.id = decorViewID;
decorView.style.display = "block";
document.body.appendChild(decorView);
}
if (!android.util.DebugUtils.DEBUG_VIEW_IN_BROWSER) {
decorView.id = this.mViewRootID + "_DecorView";
}
decorView.style.zIndex = decorZIndex;
rootView.appendChild(decorView);
*/
{
}
}
private void setOuterDimension(int width, int height) {
mWidth = width;
mHeight = height;
/**
* @j2sNative
* var rootView = document.getElementById(this.mViewRootID);
* if (rootView != null) {
* rootView.style.width = width + "px";
* rootView.style.height = height + "px";
* var appCanvas = rootView.childNodes[0];
* if (appCanvas != null) {
* if (appCanvas.width != width || appCanvas.height != height) {
* appCanvas.width = width;
* appCanvas.height = height;
* }
* }
* }
*/{}
}
private void setOuterPosition(int left, int top) {
/**
* @j2sNative
* var rootView = document.getElementById(this.mViewRootID);
* if (rootView != null) {
* rootView.style.left = left + "px";
* rootView.style.top = top + "px";
* }
*/{}
}
public void setVisibility(int visibility) {
String visible = "visible";
if (visibility != View.VISIBLE)
visible = "hidden";
/**
@j2sNative
var thisView = document.getElementById(this.mViewRootID);
if (thisView != null)
thisView.style.visibility = visible;
*/
{
}
}
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals Start");
}
if (host == null || !mAdded)
return;
mTraversalScheduled = false;
mWillDrawSoon = true;
boolean windowResizesToFitContent = false;
boolean fullRedrawNeeded = mFullRedrawNeeded;
boolean newSurface = false;
boolean surfaceChanged = false;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
int desiredWindowHeight;
int childWidthMeasureSpec;
int childHeightMeasureSpec;
final View.AttachInfo attachInfo = mAttachInfo;
final int viewVisibility = getHostVisibility();
this.setVisibility(viewVisibility);
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
surfaceChanged = true;
params = lp;
}
Rect frame = mWinFrame;
if (mFirst) {
newSurface = true;
fullRedrawNeeded = true;
mLayoutRequested = true;
DisplayMetrics packageMetrics = mView.getContext().getResources()
.getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
// System.out.println("desiredWindowWidth: " + desiredWindowWidth
// + ", desiredWindowHeight: " + desiredWindowHeight);
// For the very first time, tell the view hierarchy that it
// is attached to the window. Note that at this point the surface
// object is not initialized to its backing store, but soon it
// will be (assuming the window is visible).
attachInfo.mHasWindowFocus = false;
attachInfo.mWindowVisibility = viewVisibility;
attachInfo.mRecomputeGlobalAttributes = false;
attachInfo.mKeepScreenOn = false;
viewVisibilityChanged = false;
if (!mAttached) {
mAttached = true;
host.dispatchAttachedToWindow(attachInfo, 0);
}
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
fullRedrawNeeded = true;
mLayoutRequested = true;
windowResizesToFitContent = true;
}
}
if (viewVisibilityChanged) {
Log.d(TAG, "viewVisibilityChanged: " + viewVisibility);
attachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewVisibility == View.GONE) {
// After making a window gone, we will count it as being
// shown for the first time the next time it gets focus.
mHasHadWindowFocus = false;
}
}
boolean insetsChanged = false;
if (mLayoutRequested) {
// Execute enqueued actions on every layout in case a view that was
// detached
// enqueued an action after being detached
if (mFirst) {
// make sure touch mode code executes by setting cached value
// to opposite of the added touch mode.
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowResizesToFitContent = true;
DisplayMetrics packageMetrics = mView.getContext()
.getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
}
this.setOuterDimension(desiredWindowWidth, desiredWindowHeight);
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth,
lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight,
lp.height);
// Ask host how big it wants to be
// here our host is DecorView (extends FrameLayout)
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after measure");
}
}
if (attachInfo.mRecomputeGlobalAttributes) {
// Log.i(TAG, "Computing screen on!");
attachInfo.mRecomputeGlobalAttributes = false;
boolean oldVal = attachInfo.mKeepScreenOn;
attachInfo.mKeepScreenOn = false;
host.dispatchCollectViewAttributes(0);
if (attachInfo.mKeepScreenOn != oldVal) {
params = lp;
// Log.i(TAG, "Keep screen on changed: " +
// attachInfo.mKeepScreenOn);
}
}
if (mFirst || attachInfo.mViewVisibilityChanged) {
attachInfo.mViewVisibilityChanged = false;
int resizeMode = mSoftInputMode
& WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
// If we are in auto resize mode, then we need to determine
// what mode to use now.
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
final int N = attachInfo.mScrollContainers.size();
for (int i = 0; i < N; i++) {
if (attachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
if (resizeMode == 0) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
}
if ((lp.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
lp.softInputMode = (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
| resizeMode;
params = lp;
}
}
}
if (params != null
&& (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
if (!PixelFormat.formatHasAlpha(params.format)) {
params.format = PixelFormat.TRANSLUCENT;
}
}
boolean windowShouldResize = mLayoutRequested
&& windowResizesToFitContent
&& ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight)
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
&& frame.width() < desiredWindowWidth && frame
.width() != mWidth) || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT
&& frame.height() < desiredWindowHeight && frame
.height() != mHeight));
final boolean computesInternalInsets = attachInfo.mTreeObserver
.hasComputeInternalInsetsListeners();
boolean insetsPending = false;
int relayoutResult = 0;
if (mFirst || windowShouldResize || insetsChanged
|| viewVisibilityChanged || params != null) {
boolean contentInsetsChanged = false;
int fl = 0;
if (params != null) {
fl = params.flags;
if (attachInfo.mKeepScreenOn) {
params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
}
}
// notify focus change.
boolean visible = ((getHostVisibility() & View.VISIBILITY_MASK) == View.VISIBLE);
windowFocusChanged(visible, isInTouchMode());
if (params != null) {
relayoutResult = relayoutWindow(params, mView.mMeasuredWidth,
mView.mMeasuredHeight, viewVisibility, insetsPending, frame,
null, null);
}
if (params != null) {
params.flags = fl;
}
attachInfo.mWindowLeft = frame.left;
attachInfo.mWindowTop = frame.top;
this.setOuterDimension(frame.width(), frame.height());
// !!FIXME!! This next section handles the case where we did not get
// the
// window size we asked for. We should avoid this by getting a
// maximum size from
// the window session beforehand.
mWidth = frame.width();
mHeight = frame.height();
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
|| mHeight != host.mMeasuredHeight || contentInsetsChanged) {
childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.mMeasuredWidth;
int height = host.mMeasuredHeight;
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
System.out.println("Measure Again");
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
mLayoutRequested = true;
}
}
final boolean didLayout = mLayoutRequested;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
mScrollMayChange = true;
// layout our DecorView first because DecorView may be not at (0,
// 0).
this.setOuterPosition(attachInfo.mWindowLeft,
attachInfo.mWindowTop);
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
// By this point all views have been sized and positionned
// We can compute the transparent area
if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
}
}
if (triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes = false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
if (computesInternalInsets) {
ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
givenContent.left = givenContent.top = givenContent.right = givenContent.bottom = givenVisible.left = givenVisible.top = givenVisible.right = givenVisible.bottom = 0;
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
}
if (mFirst) {
// handle first focus request
if (mView != null) {
if (!mView.hasFocus()) {
mView.requestFocus(View.FOCUS_FORWARD);
mFocusedView = mRealFocusedView = mView.findFocus();
} else {
mRealFocusedView = mView.findFocus();
}
}
}
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mViewVisibility = viewVisibility;
if (mAttachInfo.mHasWindowFocus) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
}
}
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelDraw && !newSurface) {
mFullRedrawNeeded = false;
draw(fullRedrawNeeded || mDialogCount > 0);
} else {
// We were supposed to report when we are done drawing. Since we
// canceled the
// draw, remember it here.
if (fullRedrawNeeded) {
mFullRedrawNeeded = true;
}
if (newSurface)
newSurface = false;
// Try again
scheduleTraversals();
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals Finish");
}
}
// TODO very IMPORTANT here
int getHostVisibility() {
return mAppVisible ? mView.getVisibility() : View.GONE;
}
public void requestTransparentRegion(View child) {
if (mView == child) {
mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
// Need to make sure we re-evaluate the window attributes next
// time around, to ensure the window has the correct format.
mWindowAttributesChanged = true;
requestLayout();
}
}
/**
* Figures out the measure spec for the root view in a window based on it's
* layout params.
*
* @param windowSize
* The available width or height of the window
*
* @param rootDimension
* The layout params for one dimension (width or height) of the
* window.
*
* @return The measure spec to use to measure the root view.
*/
private int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize,
MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize,
MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that
// size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension,
MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
@Override
public void playSoundEffect(int effectId) {
// TODO Auto-generated method stub
}
@Override
public boolean performHapticFeedback(int effectId, boolean always) {
// TODO Auto-generated method stub
return false;
}
@Override
public void invalidateChild(View child, Rect dirty) {
if(DBG)System.out.println("invalidateChild");
if (mCurScrollY != 0) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
@Override
public ViewParent invalidateChildInParent(int[] location, final Rect dirty) {
invalidateChild(null, dirty);
return null;
}
public void requestChildFocus(View child, View focused) {
//checkThread();
if (mFocusedView != focused) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
scheduleTraversals();
}
mFocusedView = mRealFocusedView = focused;
if (false) Log.v(TAG, "Request child focus: focus now "
+ mFocusedView);
}
public void recomputeViewAttributes(View child) {
//checkThread();
if (mView == child) {
mAttachInfo.mRecomputeGlobalAttributes = true;
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
}
public void clearChildFocus(View child) {
//checkThread();
View oldFocus = mFocusedView;
if (false) Log.v(TAG, "Clearing child focus");
mFocusedView = mRealFocusedView = null;
if (mView != null && !mView.hasFocus()) {
// If a view gets the focus, the listener will be invoked from requestChildFocus()
if (!mView.requestFocus(View.FOCUS_FORWARD)) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
}
} else if (oldFocus != null) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
}
}
@Override
public boolean getChildVisibleRect(View child, Rect r, Point offset) {
if (child != mView) {
throw new RuntimeException("child is not mine, honest!");
}
// Note: don't apply scroll offset, because we want to know its
// visibility in the virtual canvas being given to the view hierarchy.
return r.intersect(0, 0, mWidth, mHeight);
}
/**
* {@inheritDoc}
*/
public View focusSearch(View focused, int direction) {
//checkThread();
if (!(mView instanceof ViewGroup)) {
return null;
}
return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
}
public void focusableViewAvailable(View v) {
//checkThread();
if (mView != null && !mView.hasFocus()) {
v.requestFocus();
} else {
// the one case where will transfer focus away from the current one
// is if the current view is a view group that prefers to give focus
// to its children first AND the view is a descendant of it.
mFocusedView = mView.findFocus();
boolean descendantsHaveDibsOnFocus =
(mFocusedView instanceof ViewGroup) &&
(((ViewGroup) mFocusedView).getDescendantFocusability() ==
ViewGroup.FOCUS_AFTER_DESCENDANTS);
if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
// If a view gets the focus, the listener will be invoked from requestChildFocus()
v.requestFocus();
}
}
}
/**
* Return true if child is an ancestor of parent, (or equal to the parent).
*/
private static boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
return true;
}
final ViewParent theParent = child.getParent();
return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
}
@Override
public boolean showContextMenuForChild(View originalView) {
// TODO Auto-generated method stub
return false;
}
@Override
public void childDrawableStateChanged(View child) {
// TODO Auto-generated method stub
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// TODO Auto-generated method stub
}
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
boolean immediate) {
// TODO Auto-generated method stub
return false;
}
public void setLayoutParams(WindowManager.LayoutParams attrs,
boolean newView) {
int oldSoftInputMode = mWindowAttributes.softInputMode;
// preserve compatible window flag if exists.
int compatibleWindowFlag = mWindowAttributes.flags
& WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
mWindowAttributes.copyFrom(attrs);
mWindowAttributes.flags |= compatibleWindowFlag;
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
}
// Don't lose the mode we last auto-computed.
if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
| (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
}
mWindowAttributesChanged = true;
scheduleTraversals();
}
private int relayoutWindow(WindowManager.LayoutParams params, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending, Rect outFrame,
Rect outContentInsets, Rect outVisibleInsets) {
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
// if (params != null && mTranslator != null) {
// restore = true;
// params.backup();
// mTranslator.translateWindowLayout(params);
// }
if (params != null) {
if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
}
// mPendingConfiguration.seq = 0;
// //Log.d(TAG, ">>>>>> CALLING relayout");
// int relayoutResult = mWindow.relayout(
// mWindow, params,
// (int) (mView.mMeasuredWidth * appScale + 0.5f),
// (int) (mView.mMeasuredHeight * appScale + 0.5f),
// viewVisibility, insetsPending, mWinFrame,
// mPendingContentInsets, mPendingVisibleInsets,
// mPendingConfiguration, mSurface);
// MayLoon: The relayout logic is too complex, only apply WindowManagerService.computeFrameLw here
performLayoutLockedInner(params, requestedWidth, requestedHeight, viewVisibility, insetsPending, outFrame, outContentInsets, outVisibleInsets);
//Log.d(TAG, "<<<<<< BACK FROM relayout");
// if (restore) {
// params.restore();
// }
//
// if (mTranslator != null) {
// mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
// mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
// mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
// }
int relayoutResult = 0;
if (isInTouchMode()) {
relayoutResult = 1;
}
return relayoutResult;
}
private void performLayoutLockedInner(WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending, Rect outFrame,
Rect outContentInsets, Rect outVisibleInset) {
if (viewVisibility == View.GONE) {
return;
}
//beginLayoutLw(dw, dh));
// The current size of the screen.
int mW, mH;
// During layout, the current screen borders with all outer decoration
// (status bar, input method dock) accounted for.
int mCurLeft, mCurTop, mCurRight, mCurBottom;
// During layout, the frame in which content should be displayed
// to the user, accounting for all screen decoration except for any
// space they deem as available for other content. This is usually
// the same as mCur*, but may be larger if the screen decor has supplied
// content insets.
int mContentLeft, mContentTop, mContentRight, mContentBottom;
// During layout, the current screen borders along with input method
// windows are placed.
int mDockLeft, mDockTop, mDockRight, mDockBottom;
DisplayMetrics packageMetrics = mView.getContext().getResources()
.getDisplayMetrics();
mW = packageMetrics.widthPixels;
mH = packageMetrics.heightPixels;
mDockLeft = mContentLeft = mCurLeft = 0;
mDockTop = mContentTop = mCurTop = 0;
mDockRight = mContentRight = mCurRight = mW;
mDockBottom = mContentBottom = mCurBottom = mH;
// decide where the status bar goes ahead of time
// if (mStatusBar != null) {
// final Rect pf = mTmpParentFrame;
// final Rect df = mTmpDisplayFrame;
// final Rect vf = mTmpVisibleFrame;
// pf.left = df.left = vf.left = 0;
// pf.top = df.top = vf.top = 0;
// pf.right = df.right = vf.right = displayWidth;
// pf.bottom = df.bottom = vf.bottom = displayHeight;
//
// mStatusBar.computeFrameLw(pf, df, vf, vf);
// if (mStatusBar.isVisibleLw()) {
// // If the status bar is hidden, we don't want to cause
// // windows behind it to scroll.
// mDockTop = mContentTop = mCurTop = mStatusBar.getFrameLw().bottom;
// if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mDockBottom="
// + mDockBottom + " mContentBottom="
// + mContentBottom + " mCurBottom=" + mCurBottom);
// }
// }
final int fl = attrs.flags;
final int sim = attrs.softInputMode;
final Rect pf = new Rect();
final Rect df = new Rect();
final Rect cf = new Rect();
final Rect vf = new Rect();
boolean attached = false;//attached != null
if (attrs.type == TYPE_INPUT_METHOD) {
pf.left = df.left = cf.left = vf.left = mDockLeft;
pf.top = df.top = cf.top = vf.top = mDockTop;
pf.right = df.right = cf.right = vf.right = mDockRight;
pf.bottom = df.bottom = cf.bottom = vf.bottom = mDockBottom;
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
//mDockLayer = win.getSurfaceLayer();
} else {
if ((fl &
(FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
// This is the case for a normal activity window: we want it
// to cover all of the screen space, and it can take care of
// moving its contents to account for screen decorations that
// intrude into that space.
if (attached) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
//setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
} else {
pf.left = df.left = 0;
pf.top = df.top = 0;
pf.right = df.right = mW;
pf.bottom = df.bottom = mH;
if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
cf.left = mDockLeft;
cf.top = mDockTop;
cf.right = mDockRight;
cf.bottom = mDockBottom;
} else {
cf.left = mContentLeft;
cf.top = mContentTop;
cf.right = mContentRight;
cf.bottom = mContentBottom;
}
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
}
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) {
// A window that has requested to fill the entire screen just
// gets everything, period.
pf.left = df.left = cf.left = 0;
pf.top = df.top = cf.top = 0;
pf.right = df.right = cf.right = mW;
pf.bottom = df.bottom = cf.bottom = mH;
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
} else if (attached) {
// A child window should be placed inside of the same visible
// frame that its parent had.
//setAttachedWindowFrames(win, fl, sim, attached, false, pf, df, cf, vf);
} else {
// Otherwise, a normal window must be placed inside the content
// of all screen decorations.
pf.left = mContentLeft;
pf.top = mContentTop;
pf.right = mContentRight;
pf.bottom = mContentBottom;
if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
df.left = cf.left = mDockLeft;
df.top = cf.top = mDockTop;
df.right = cf.right = mDockRight;
df.bottom = cf.bottom = mDockBottom;
} else {
df.left = cf.left = mContentLeft;
df.top = cf.top = mContentTop;
df.right = cf.right = mContentRight;
df.bottom = cf.bottom = mContentBottom;
}
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
}
}
// if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0) {
// df.left = df.top = cf.left = cf.top = vf.left = vf.top = -10000;
// df.right = df.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
// }
computeFrameLw(attrs, requestedWidth, requestedHeight, outFrame, outContentInsets, outVisibleInset, pf, df, cf, vf);
}
public void computeFrameLw(WindowManager.LayoutParams mAttrs, int requestedWidth,
int requestedHeight, Rect outFrame, Rect outContentInsets, Rect outVisibleInset, Rect pf, Rect df, Rect cf, Rect vf) {
int mRequestedWidth = requestedWidth;
int mRequestedHeight = requestedHeight;
final Rect container = new Rect();
container.set(pf);
final Rect display = new Rect();
display.set(df);
// if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW) != 0) {
// container.intersect(mCompatibleScreenFrame);
// if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) == 0) {
// display.intersect(mCompatibleScreenFrame);
// }
// }
final int pw = container.right - container.left;
final int ph = container.bottom - container.top;
int w,h;
if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {
w = mAttrs.width < 0 ? pw : mAttrs.width;
h = mAttrs.height< 0 ? ph : mAttrs.height;
} else {
w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth;
h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
}
final Rect content = new Rect();
content.set(cf);
final Rect visible = new Rect();;
visible.set(vf);
final Rect frame = new Rect();
// final int fw = frame.width();
// final int fh = frame.height();
//System.out.println("In: w=" + w + " h=" + h + " container=" +
// container + " x=" + mAttrs.x + " y=" + mAttrs.y);
Gravity.apply(mAttrs.gravity, w, h, container,
(int) (mAttrs.x + mAttrs.horizontalMargin * pw),
(int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);
// Now make sure the window fits in the overall display.
Gravity.applyDisplay(mAttrs.gravity, df, frame);
// Make sure the content and visible frames are inside of the
// final window frame.
if (content.left < frame.left) content.left = frame.left;
if (content.top < frame.top) content.top = frame.top;
if (content.right > frame.right) content.right = frame.right;
if (content.bottom > frame.bottom) content.bottom = frame.bottom;
if (visible.left < frame.left) visible.left = frame.left;
if (visible.top < frame.top) visible.top = frame.top;
if (visible.right > frame.right) visible.right = frame.right;
if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
final Rect contentInsets = new Rect();
contentInsets.left = content.left-frame.left;
contentInsets.top = content.top-frame.top;
contentInsets.right = frame.right-content.right;
contentInsets.bottom = frame.bottom-content.bottom;
final Rect visibleInsets = new Rect();
visibleInsets.left = visible.left-frame.left;
visibleInsets.top = visible.top-frame.top;
visibleInsets.right = frame.right-visible.right;
visibleInsets.bottom = frame.bottom-visible.bottom;
// if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
// updateWallpaperOffsetLocked(this, mDisplay.getWidth(),
// mDisplay.getHeight(), false);
// }
if (outFrame != null) outFrame.set(frame);
if (outContentInsets != null) outContentInsets.set(contentInsets);
if (outVisibleInset != null) outVisibleInset.set(visibleInsets);
}
public void die(boolean b) {
// if (mAdded && !mFirst) {
// int viewVisibility = mView.getVisibility();
// boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
// if (mWindowAttributesChanged || viewVisibilityChanged) {
// // If layout params have been changed, first give them
// // to the window manager to make sure it has the correct
// // animation info.
// try {
// if ((relayoutWindow(mWindowAttributes, viewVisibility,
// false) & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
// sWindowSession.finishDrawing(mWindow);
// }
// } catch (RemoteException e) {
// }
// }
//
// mSurface.release();
// }
if (mAdded) {
mAdded = false;
dispatchDetachedFromWindow();
}
}
void dispatchDetachedFromWindow() {
Log.i(TAG, "dispatchDetachedFromWindow");
if (mView != null && mAttached) {
mView.dispatchDetachedFromWindow();
mAttached = false;
}
mView = null;
mAttachInfo.mRootView = null;
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueueCallback = null;
} else {
InputQueue.unregisterInputChannel(mInputChannel);
}
}
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
reset();
}
static float gPrevDur = 0;
private void draw(boolean fullRedrawNeeded) {
if (mAttachInfo.mViewScrollChanged) {
mAttachInfo.mViewScrollChanged = false;
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
int yoff = mScrollY;
if (mCurScrollY != yoff) {
mCurScrollY = yoff;
fullRedrawNeeded = true;
}
float appScale = mAttachInfo.mApplicationScale;
Rect dirty = mDirty;
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.union(0, 0, (int) (mWidth * appScale), (int) (mHeight * appScale));
}
mCanvas.setDimension(mWidth, mHeight);
if (!dirty.isEmpty() || mIsAnimating) {
Canvas canvas;
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
canvas = mCanvas;
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
//if (/*!canvas.isOpaque() || */yoff != 0) {
// Clear the background first.
// canvas.chooseCanvas(Canvas.BACKGROUND_CANVAS);
// canvas.drawColor(0);
//}
dirty.setEmpty();
mIsAnimating = false;
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mView.mPrivateFlags |= View.DRAWN;
int saveCount = canvas.save();
try {
canvas.clipRect(left, top, right, bottom);
canvas.translate(0, -yoff);
mView.draw(canvas);
} finally {
mAttachInfo.mIgnoreDirtyState = false;
canvas.restoreToCount(saveCount);
}
}
long over = System.currentTimeMillis();
Log.d(TAG,"draw over at time:" + over);
if (DBG_FPS) {
long now = System.currentTimeMillis();
if (sDrawTime != 0) {
float duration = now - sDrawTime;
duration = (gPrevDur + duration) / 2;
gPrevDur = duration;
duration = 1000 / duration;
Log.w(TAG, "Show FPS: " + duration);
}
sDrawTime = now;
}
}
public final static int DO_TRAVERSAL = 1000;
public final static int DIE = 1001;
public final static int RESIZED = 1002;
public final static int RESIZED_REPORT = 1003;
public final static int WINDOW_FOCUS_CHANGED = 1004;
public final static int DISPATCH_KEY = 1005;
public final static int DISPATCH_POINTER = 1006;
public final static int DISPATCH_TRACKBALL = 1007;
public final static int DISPATCH_APP_VISIBILITY = 1008;
public final static int DISPATCH_GET_NEW_SURFACE = 1009;
public final static int FINISHED_EVENT = 1010;
public final static int DISPATCH_KEY_FROM_IME = 1011;
public final static int FINISH_INPUT_CONNECTION = 1012;
public final static int CHECK_FOCUS = 1013;
public final static int CLOSE_SYSTEM_DIALOGS = 1014;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case View.AttachInfo.INVALIDATE_MSG:
((View) msg.obj).invalidate();
break;
case View.AttachInfo.INVALIDATE_RECT_MSG:
final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.release();
break;
case DO_TRAVERSAL:
// if (mProfile) {
// Debug.startMethodTracing("ViewRoot");
// }
performTraversals();
// if (mProfile) {
// Debug.stopMethodTracing();
// mProfile = false;
// }
break;
case FINISHED_EVENT:
//handleFinishedEvent(msg.arg1, msg.arg2 != 0);
Log.e(TAG, "FINISHED_EVENT is not handled now!");
break;
case DISPATCH_KEY:
// if (LOCAL_LOGV) Log.v(
// TAG, "Dispatching key "
// + msg.obj + " to " + mView);
// deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
Log.e(TAG, "DISPATCH_KEY is not handled now!");
break;
case DISPATCH_POINTER: {
MotionEvent event = (MotionEvent) msg.obj;
// try {
// deliverPointerEvent(event);
// } finally {
// event.recycle();
// if (msg.arg1 != 0) {
// finishInputEvent();
// }
// if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
// }
Log.e(TAG, "DISPATCH_POINTER is not handled now!");
} break;
case DISPATCH_TRACKBALL: {
MotionEvent event = (MotionEvent) msg.obj;
// try {
// deliverTrackballEvent(event);
// } finally {
// event.recycle();
// if (msg.arg1 != 0) {
// finishInputEvent();
// }
// }
Log.e(TAG, "DISPATCH_TRACKBALL is not handled now!");
} break;
case DISPATCH_APP_VISIBILITY:
//handleAppVisibility(msg.arg1 != 0);
Log.e(TAG, "DISPATCH_APP_VISIBILITY is not handled now!");
break;
case DISPATCH_GET_NEW_SURFACE:
//handleGetNewSurface();
Log.e(TAG, "DISPATCH_GET_NEW_SURFACE is not handled now!");
break;
case RESIZED:
// ResizedInfo ri = (ResizedInfo)msg.obj;
//
// if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
// && mPendingContentInsets.equals(ri.coveredInsets)
// && mPendingVisibleInsets.equals(ri.visibleInsets)
// && ((ResizedInfo)msg.obj).newConfig == null) {
// break;
// }
Log.e(TAG, "RESIZED is not handled now!");
// fall through...
case RESIZED_REPORT:
// if (mAdded) {
// Configuration config = ((ResizedInfo)msg.obj).newConfig;
// if (config != null) {
// updateConfiguration(config, false);
// }
// mWinFrame.left = 0;
// mWinFrame.right = msg.arg1;
// mWinFrame.top = 0;
// mWinFrame.bottom = msg.arg2;
// mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets);
// mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets);
// if (msg.what == RESIZED_REPORT) {
// mReportNextDraw = true;
// }
//
// if (mView != null) {
// forceLayout(mView);
// }
// requestLayout();
// }
Log.e(TAG, "RESIZED_REPORT is not handled now!");
break;
case WINDOW_FOCUS_CHANGED: {
if (mAdded) {
boolean hasWindowFocus = msg.arg1 != 0;
mAttachInfo.mHasWindowFocus = hasWindowFocus;
if (hasWindowFocus) {
boolean inTouchMode = msg.arg2 != 0;
ensureTouchModeLocally(inTouchMode);
}
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
// InputMethodManager imm = InputMethodManager.peekInstance();
if (mView != null) {
// if (hasWindowFocus && imm != null && mLastWasImTarget) {
// imm.startGettingWindowFocus(mView);
// }
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
}
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
if (hasWindowFocus) {
// if (imm != null && mLastWasImTarget) {
// imm.onWindowFocus(mView, mView.findFocus(),
// mWindowAttributes.softInputMode,
// !mHasHadWindowFocus, mWindowAttributes.flags);
// }
// Clear the forward bit. We can just do this directly, since
// the window manager doesn't care about it.
mWindowAttributes.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
((WindowManager.LayoutParams)mView.getLayoutParams())
.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
}
// if (hasWindowFocus && mView != null) {
// sendAccessibilityEvents();
// }
}
} break;
case DIE:
//doDie();
Log.e(TAG, "DIE is not handled now!");
break;
case DISPATCH_KEY_FROM_IME: {
// if (LOCAL_LOGV) Log.v(
// TAG, "Dispatching key "
// + msg.obj + " from IME to " + mView);
// KeyEvent event = (KeyEvent)msg.obj;
// if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
// // The IME is trying to say this event is from the
// // system! Bad bad bad!
// event = KeyEvent.changeFlags(event,
// event.getFlags()&~KeyEvent.FLAG_FROM_SYSTEM);
// }
// deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
Log.e(TAG, "DISPATCH_KEY_FROM_IME is not handled now!");
} break;
case FINISH_INPUT_CONNECTION: {
// InputMethodManager imm = InputMethodManager.peekInstance();
// if (imm != null) {
// imm.reportFinishInputConnection((InputConnection)msg.obj);
// }
Log.e(TAG, "FINISH_INPUT_CONNECTION is not handled now!");
} break;
case CHECK_FOCUS: {
// InputMethodManager imm = InputMethodManager.peekInstance();
// if (imm != null) {
// imm.checkFocus();
// }
Log.e(TAG, "CHECK_FOCUS is not handled now!");
} break;
case CLOSE_SYSTEM_DIALOGS: {
// if (mView != null) {
// mView.onCloseSystemDialogs((String)msg.obj);
// }
Log.e(TAG, "CLOSE_SYSTEM_DIALOGS is not handled now!");
} break;
}
}
/**
* Something in the current window tells us we need to change the touch mode. For
* example, we are not in touch mode, and the user touches the screen.
*
* If the touch mode has changed, tell the window manager, and handle it locally.
*
* @param inTouchMode Whether we want to be in touch mode.
* @return True if the touch mode changed and focus changed was changed as a result
*/
boolean ensureTouchMode(boolean inTouchMode) {
if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
+ "touch mode is " + mAttachInfo.mInTouchMode);
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
// tell the window manager
// try {
// sWindowSession.setInTouchMode(inTouchMode);
// } catch (RemoteException e) {
// throw new RuntimeException(e);
// }
WindowManagerImpl mWindowManager = (WindowManagerImpl)WindowManagerImpl.getDefault();
mWindowManager.setInTouchMode(inTouchMode);
// handle the change
return ensureTouchModeLocally(inTouchMode);
}
/**
* Ensure that the touch mode for this window is set, and if it is changing,
* take the appropriate action.
* @param inTouchMode Whether we want to be in touch mode.
* @return True if the touch mode changed and focus changed was changed as a result
*/
private boolean ensureTouchModeLocally(boolean inTouchMode) {
if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
+ "touch mode is " + mAttachInfo.mInTouchMode);
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
mAttachInfo.mInTouchMode = inTouchMode;
mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
}
private boolean enterTouchMode() {
if (mView != null) {
if (mView.hasFocus()) {
// note: not relying on mFocusedView here because this could
// be when the window is first being added, and mFocused isn't
// set yet.
final View focused = mView.findFocus();
if (focused != null && !focused.isFocusableInTouchMode()) {
final ViewGroup ancestorToTakeFocus =
findAncestorToTakeFocusInTouchMode(focused);
if (ancestorToTakeFocus != null) {
// there is an ancestor that wants focus after its descendants that
// is focusable in touch mode.. give it focus
return ancestorToTakeFocus.requestFocus();
} else {
// nothing appropriate to have focus in touch mode, clear it out
mView.unFocus();
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
mFocusedView = null;
return true;
}
}
}
}
return false;
}
/**
* Find an ancestor of focused that wants focus after its descendants and is
* focusable in touch mode.
* @param focused The currently focused view.
* @return An appropriate view, or null if no such view exists.
*/
private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
ViewParent parent = focused.getParent();
while (parent instanceof ViewGroup) {
final ViewGroup vgParent = (ViewGroup) parent;
if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
&& vgParent.isFocusableInTouchMode()) {
return vgParent;
}
if (vgParent.isRootNamespace()) {
return null;
} else {
parent = vgParent.getParent();
}
}
return null;
}
private boolean leaveTouchMode() {
if (mView != null) {
if (mView.hasFocus()) {
// i learned the hard way to not trust mFocusedView :)
mFocusedView = mView.findFocus();
if (!(mFocusedView instanceof ViewGroup)) {
// some view has focus, let it keep it
return false;
} else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
ViewGroup.FOCUS_AFTER_DESCENDANTS) {
// some view group has focus, and doesn't prefer its children
// over itself for focus, so let them keep it.
return false;
}
}
// find the best view to give focus to in this brave new non-touch-mode
// world
final View focused = focusSearch(null, View.FOCUS_DOWN);
if (focused != null) {
return focused.requestFocus(View.FOCUS_DOWN);
}
}
return false;
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
Message msg = Message.obtain();
msg.what = WINDOW_FOCUS_CHANGED;
msg.arg1 = hasFocus ? 1 : 0;
msg.arg2 = inTouchMode ? 1 : 0;
sendMessage(msg);
}
public void reset() {
Log.d(TAG, "In reset");
viewRootList.remove(this);
/**
@j2sNative
var rootView = document.getElementById(this.mViewRootID);
if (rootView != null) {
rootView.parentNode.removeChild(rootView);
}
*/
{
}
}
static RunQueue getRunQueue() {
RunQueue rq = sRunQueues;
if (rq != null) {
return rq;
}
rq = new RunQueue();
sRunQueues = rq;
return rq;
}
/**
* @hide
*/
static final class RunQueue {
private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
void post(Runnable action) {
postDelayed(action, 0);
}
void postDelayed(Runnable action, long delayMillis) {
HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
handlerAction.delay = delayMillis;
synchronized (mActions) {
mActions.add(handlerAction);
}
}
void removeCallbacks(Runnable action) {
final HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
while (actions.remove(handlerAction)) {
// Keep going
}
}
}
void executeActions(Handler handler) {
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
final int count = actions.size();
for (int i = 0; i < count; i++) {
final HandlerAction handlerAction = actions.get(i);
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
actions.clear();
}
}
private static class HandlerAction {
Runnable action;
long delay;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HandlerAction that = (HandlerAction) o;
return !(action != null ? !action.equals(that.action) : that.action != null);
}
@Override
public int hashCode() {
int result = action != null ? action.hashCode() : 0;
result = 31 * result + (int) (delay ^ (delay >>> 32));
return result;
}
}
}
static class W extends Window {
private final WeakReference<ViewRoot> mViewRoot;
public W(ViewRoot viewRoot, Context context) {
super(context);
mViewRoot = new WeakReference<ViewRoot>(viewRoot);
}
public void resized(int w, int h, Rect coveredInsets,
Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
final ViewRoot viewRoot = mViewRoot.get();
}
public void dispatchAppVisibility(boolean visible) {
final ViewRoot viewRoot = mViewRoot.get();
}
public void dispatchGetNewSurface() {
final ViewRoot viewRoot = mViewRoot.get();
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRoot viewRoot = mViewRoot.get();
}
private static int checkCallingPermission(String permission) {
return 0;
}
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
final ViewRoot viewRoot = mViewRoot.get();
}
public void closeSystemDialogs(String reason) {
}
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
boolean sync) {
}
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
}
}
}