Split WindowManagerImpl into two parts, the WindowManager interface implementation remains where it is but the global communications with the window manager are now handled by the WindowManagerGlobal class. This change greatly simplifies the challenge of having separate WindowManager instances for each Context. Removed WindowManagerImpl.getDefault(). This represents the bulk of this change. Most of the usages of this method were either to perform global functions (now handled by WindowManagerGlobal) or to obtain the default display (now handled by DisplayManager). Explicitly associate each new window with a display and make the Display object available to the View hierarchy. Add stubs for some new display manager API features. Start to split apart the concepts of display id and layer stack. since they operate at different layers of abstraction. While it's true that each logical display uniquely corresponds to a surface flinger layer stack, it is not necessarily the case that they must use the same ids. Added Display.getLayerStack() and started using it in places where it was relatively easy to do. Change-Id: I29ed909114dec86807c4d3a5059c3fa0358bea61
459 lines
17 KiB
Java
459 lines
17 KiB
Java
/*
|
|
* Copyright (C) 2012 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.systemui;
|
|
|
|
import android.app.ActivityManager;
|
|
import android.content.Context;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.PixelFormat;
|
|
import android.os.RemoteException;
|
|
import android.util.Log;
|
|
import android.util.Slog;
|
|
import android.view.Choreographer;
|
|
import android.view.Display;
|
|
import android.view.IWindowSession;
|
|
import android.view.MotionEvent;
|
|
import android.view.VelocityTracker;
|
|
import android.view.View;
|
|
import android.view.ViewRootImpl;
|
|
import android.view.WindowManager;
|
|
import android.view.WindowManagerGlobal;
|
|
import android.view.animation.Transformation;
|
|
import android.widget.FrameLayout;
|
|
|
|
public class UniverseBackground extends FrameLayout {
|
|
static final String TAG = "UniverseBackground";
|
|
static final boolean SPEW = false;
|
|
static final boolean CHATTY = false;
|
|
|
|
final IWindowSession mSession;
|
|
final View mContent;
|
|
final View mBottomAnchor;
|
|
|
|
final Runnable mAnimationCallback = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
doAnimation(mChoreographer.getFrameTimeNanos());
|
|
}
|
|
};
|
|
|
|
// fling gesture tuning parameters, scaled to display density
|
|
private float mSelfExpandVelocityPx; // classic value: 2000px/s
|
|
private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
|
|
private float mFlingExpandMinVelocityPx; // classic value: 200px/s
|
|
private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
|
|
private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
|
|
private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
|
|
private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
|
|
|
|
private float mExpandAccelPx; // classic value: 2000px/s/s
|
|
private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
|
|
|
|
static final int STATE_CLOSED = 0;
|
|
static final int STATE_OPENING = 1;
|
|
static final int STATE_OPEN = 2;
|
|
private int mState = STATE_CLOSED;
|
|
|
|
private float mDragStartX, mDragStartY;
|
|
private float mAverageX, mAverageY;
|
|
|
|
// position
|
|
private int[] mPositionTmp = new int[2];
|
|
private boolean mExpanded;
|
|
private boolean mExpandedVisible;
|
|
|
|
private boolean mTracking;
|
|
private VelocityTracker mVelocityTracker;
|
|
|
|
private Choreographer mChoreographer;
|
|
private boolean mAnimating;
|
|
private boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
|
|
private float mAnimY;
|
|
private float mAnimVel;
|
|
private float mAnimAccel;
|
|
private long mAnimLastTimeNanos;
|
|
private boolean mAnimatingReveal = false;
|
|
|
|
private int mYDelta = 0;
|
|
private Transformation mUniverseTransform = new Transformation();
|
|
private final float[] mTmpFloats = new float[9];
|
|
|
|
public UniverseBackground(Context context) {
|
|
super(context);
|
|
setBackgroundColor(0xff000000);
|
|
mSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
|
|
mContent = View.inflate(context, R.layout.universe, null);
|
|
addView(mContent);
|
|
mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
|
|
@Override public void onClick(View v) {
|
|
animateCollapse();
|
|
}
|
|
});
|
|
mBottomAnchor = mContent.findViewById(R.id.bottom);
|
|
mChoreographer = Choreographer.getInstance();
|
|
loadDimens();
|
|
}
|
|
|
|
@Override
|
|
protected void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
loadDimens();
|
|
}
|
|
|
|
private void loadDimens() {
|
|
final Resources res = getContext().getResources();
|
|
mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
|
|
mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
|
|
mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
|
|
mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
|
|
|
|
mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
|
|
mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
|
|
|
|
mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
|
|
mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
|
|
|
|
mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
|
|
}
|
|
|
|
private void computeAveragePos(MotionEvent event) {
|
|
final int num = event.getPointerCount();
|
|
float x = 0, y = 0;
|
|
for (int i=0; i<num; i++) {
|
|
x += event.getX(i);
|
|
y += event.getY(i);
|
|
}
|
|
mAverageX = x / num;
|
|
mAverageY = y / num;
|
|
}
|
|
|
|
private void sendUniverseTransform() {
|
|
if (getWindowToken() != null) {
|
|
mUniverseTransform.getMatrix().getValues(mTmpFloats);
|
|
try {
|
|
mSession.setUniverseTransform(getWindowToken(), mUniverseTransform.getAlpha(),
|
|
mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y],
|
|
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
|
|
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
public WindowManager.LayoutParams getLayoutParams() {
|
|
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
|
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
|
|
WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND,
|
|
0
|
|
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
|
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
|
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
|
|
PixelFormat.OPAQUE);
|
|
// this will allow the window to run in an overlay on devices that support this
|
|
if (ActivityManager.isHighEndGfx()) {
|
|
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
|
}
|
|
lp.setTitle("UniverseBackground");
|
|
lp.windowAnimations = 0;
|
|
return lp;
|
|
}
|
|
|
|
private int getExpandedViewMaxHeight() {
|
|
return mBottomAnchor.getTop();
|
|
}
|
|
|
|
public void animateCollapse() {
|
|
animateCollapse(1.0f);
|
|
}
|
|
|
|
public void animateCollapse(float velocityMultiplier) {
|
|
if (SPEW) {
|
|
Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
|
|
+ " mExpandedVisible=" + mExpandedVisible
|
|
+ " mExpanded=" + mExpanded
|
|
+ " mAnimating=" + mAnimating
|
|
+ " mAnimY=" + mAnimY
|
|
+ " mAnimVel=" + mAnimVel);
|
|
}
|
|
|
|
mState = STATE_CLOSED;
|
|
if (!mExpandedVisible) {
|
|
return;
|
|
}
|
|
|
|
int y;
|
|
if (mAnimating) {
|
|
y = (int)mAnimY;
|
|
} else {
|
|
y = getExpandedViewMaxHeight()-1;
|
|
}
|
|
// Let the fling think that we're open so it goes in the right direction
|
|
// and doesn't try to re-open the windowshade.
|
|
mExpanded = true;
|
|
prepareTracking(y, false);
|
|
performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
|
|
}
|
|
|
|
private void updateUniverseScale() {
|
|
if (mYDelta > 0) {
|
|
int w = getWidth();
|
|
int h = getHeight();
|
|
float scale = (h-mYDelta+.5f) / (float)h;
|
|
mUniverseTransform.getMatrix().setScale(scale, scale, w/2, h);
|
|
if (CHATTY) Log.i(TAG, "w=" + w + " h=" + h + " scale=" + scale
|
|
+ ": " + mUniverseTransform);
|
|
sendUniverseTransform();
|
|
if (getVisibility() != VISIBLE) {
|
|
setVisibility(VISIBLE);
|
|
}
|
|
} else {
|
|
if (CHATTY) Log.i(TAG, "mYDelta=" + mYDelta);
|
|
mUniverseTransform.clear();
|
|
sendUniverseTransform();
|
|
if (getVisibility() == VISIBLE) {
|
|
setVisibility(GONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void resetLastAnimTime() {
|
|
mAnimLastTimeNanos = System.nanoTime();
|
|
if (SPEW) {
|
|
Throwable t = new Throwable();
|
|
t.fillInStackTrace();
|
|
Slog.d(TAG, "resetting last anim time=" + mAnimLastTimeNanos, t);
|
|
}
|
|
}
|
|
|
|
void doAnimation(long frameTimeNanos) {
|
|
if (mAnimating) {
|
|
if (SPEW) Slog.d(TAG, "doAnimation dt=" + (frameTimeNanos - mAnimLastTimeNanos));
|
|
if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
|
|
incrementAnim(frameTimeNanos);
|
|
if (SPEW) {
|
|
Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
|
|
}
|
|
|
|
if (mAnimY >= getExpandedViewMaxHeight()-1 && !mClosing) {
|
|
if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
|
|
mAnimating = false;
|
|
mYDelta = getExpandedViewMaxHeight();
|
|
updateUniverseScale();
|
|
mExpanded = true;
|
|
mState = STATE_OPEN;
|
|
return;
|
|
}
|
|
|
|
if (mAnimY <= 0 && mClosing) {
|
|
if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
|
|
mAnimating = false;
|
|
mYDelta = 0;
|
|
updateUniverseScale();
|
|
mExpanded = false;
|
|
mState = STATE_CLOSED;
|
|
return;
|
|
}
|
|
|
|
mYDelta = (int)mAnimY;
|
|
updateUniverseScale();
|
|
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
|
|
mAnimationCallback, null);
|
|
}
|
|
}
|
|
|
|
void stopTracking() {
|
|
mTracking = false;
|
|
mVelocityTracker.recycle();
|
|
mVelocityTracker = null;
|
|
}
|
|
|
|
void incrementAnim(long frameTimeNanos) {
|
|
final long deltaNanos = Math.max(frameTimeNanos - mAnimLastTimeNanos, 0);
|
|
final float t = deltaNanos * 0.000000001f; // ns -> s
|
|
final float y = mAnimY;
|
|
final float v = mAnimVel; // px/s
|
|
final float a = mAnimAccel; // px/s/s
|
|
mAnimY = y + (v*t) + (0.5f*a*t*t); // px
|
|
mAnimVel = v + (a*t); // px/s
|
|
mAnimLastTimeNanos = frameTimeNanos; // ns
|
|
//Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
|
|
// + " mAnimAccel=" + mAnimAccel);
|
|
}
|
|
|
|
void prepareTracking(int y, boolean opening) {
|
|
if (CHATTY) {
|
|
Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
|
|
}
|
|
|
|
mTracking = true;
|
|
mVelocityTracker = VelocityTracker.obtain();
|
|
if (opening) {
|
|
mAnimAccel = mExpandAccelPx;
|
|
mAnimVel = mFlingExpandMinVelocityPx;
|
|
mAnimY = y;
|
|
mAnimating = true;
|
|
mAnimatingReveal = true;
|
|
resetLastAnimTime();
|
|
mExpandedVisible = true;
|
|
}
|
|
if (mAnimating) {
|
|
mAnimating = false;
|
|
mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
|
|
mAnimationCallback, null);
|
|
}
|
|
}
|
|
|
|
void performFling(int y, float vel, boolean always) {
|
|
if (CHATTY) {
|
|
Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel);
|
|
}
|
|
|
|
mAnimatingReveal = false;
|
|
|
|
mAnimY = y;
|
|
mAnimVel = vel;
|
|
|
|
//Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
|
|
|
|
if (mExpanded) {
|
|
if (!always && (
|
|
vel > mFlingCollapseMinVelocityPx
|
|
|| (y > (getExpandedViewMaxHeight()*(1f-mCollapseMinDisplayFraction)) &&
|
|
vel > -mFlingExpandMinVelocityPx))) {
|
|
// We are expanded, but they didn't move sufficiently to cause
|
|
// us to retract. Animate back to the expanded position.
|
|
mAnimAccel = mExpandAccelPx;
|
|
if (vel < 0) {
|
|
mAnimVel = 0;
|
|
}
|
|
}
|
|
else {
|
|
// We are expanded and are now going to animate away.
|
|
mAnimAccel = -mCollapseAccelPx;
|
|
if (vel > 0) {
|
|
mAnimVel = 0;
|
|
}
|
|
}
|
|
} else {
|
|
if (always || (
|
|
vel > mFlingExpandMinVelocityPx
|
|
|| (y > (getExpandedViewMaxHeight()*(1f-mExpandMinDisplayFraction)) &&
|
|
vel > -mFlingCollapseMinVelocityPx))) {
|
|
// We are collapsed, and they moved enough to allow us to
|
|
// expand. Animate in the notifications.
|
|
mAnimAccel = mExpandAccelPx;
|
|
if (vel < 0) {
|
|
mAnimVel = 0;
|
|
}
|
|
}
|
|
else {
|
|
// We are collapsed, but they didn't move sufficiently to cause
|
|
// us to retract. Animate back to the collapsed position.
|
|
mAnimAccel = -mCollapseAccelPx;
|
|
if (vel > 0) {
|
|
mAnimVel = 0;
|
|
}
|
|
}
|
|
}
|
|
//Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
|
|
// + " mAnimAccel=" + mAnimAccel);
|
|
|
|
resetLastAnimTime();
|
|
mAnimating = true;
|
|
mClosing = mAnimAccel < 0;
|
|
mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
|
|
mAnimationCallback, null);
|
|
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
|
|
mAnimationCallback, null);
|
|
|
|
stopTracking();
|
|
}
|
|
|
|
private void trackMovement(MotionEvent event) {
|
|
mVelocityTracker.addMovement(event);
|
|
}
|
|
|
|
public boolean consumeEvent(MotionEvent event) {
|
|
if (mState == STATE_CLOSED) {
|
|
if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
|
|
// Second finger down, time to start opening!
|
|
computeAveragePos(event);
|
|
mDragStartX = mAverageX;
|
|
mDragStartY = mAverageY;
|
|
mYDelta = 0;
|
|
mUniverseTransform.clear();
|
|
sendUniverseTransform();
|
|
setVisibility(VISIBLE);
|
|
mState = STATE_OPENING;
|
|
prepareTracking((int)mDragStartY, true);
|
|
mVelocityTracker.clear();
|
|
trackMovement(event);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (mState == STATE_OPENING) {
|
|
if (event.getActionMasked() == MotionEvent.ACTION_UP
|
|
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
|
|
mVelocityTracker.computeCurrentVelocity(1000);
|
|
computeAveragePos(event);
|
|
|
|
float yVel = mVelocityTracker.getYVelocity();
|
|
boolean negative = yVel < 0;
|
|
|
|
float xVel = mVelocityTracker.getXVelocity();
|
|
if (xVel < 0) {
|
|
xVel = -xVel;
|
|
}
|
|
if (xVel > mFlingGestureMaxXVelocityPx) {
|
|
xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
|
|
}
|
|
|
|
float vel = (float)Math.hypot(yVel, xVel);
|
|
if (negative) {
|
|
vel = -vel;
|
|
}
|
|
|
|
if (CHATTY) {
|
|
Slog.d(TAG, String.format("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
|
|
mVelocityTracker.getXVelocity(),
|
|
mVelocityTracker.getYVelocity(),
|
|
xVel, yVel,
|
|
vel));
|
|
}
|
|
|
|
performFling((int)mAverageY, vel, false);
|
|
mState = STATE_OPEN;
|
|
return true;
|
|
}
|
|
|
|
computeAveragePos(event);
|
|
mYDelta = (int)(mAverageY - mDragStartY);
|
|
if (mYDelta > getExpandedViewMaxHeight()) {
|
|
mYDelta = getExpandedViewMaxHeight();
|
|
}
|
|
updateUniverseScale();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|