diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index d596a7f9bb189..e967522170a75 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -373,6 +373,19 @@ public class Surface implements Parcelable { setOrientation(display, orientation, 0); } + /** + * Copy the current screen contents into a bitmap and return it. + * + * @param width The desired width of the returned bitmap; the raw + * screen will be scaled down to this size. + * @param height The desired height of the returned bitmap; the raw + * screen will be scaled down to this size. + * @return Returns a Bitmap containing the screen contents. + * + * @hide + */ + public static native Bitmap screenshot(int width, int height); + /** * set surface parameters. * needs to be inside open/closeTransaction block diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index c4d6d1b7ab49f..9a85edcde0d46 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -19,7 +19,9 @@ #include #include "android_util_Binder.h" +#include "android/graphics/GraphicsJNI.h" +#include #include #include #include @@ -91,15 +93,6 @@ struct no_t { static no_t no; -static __attribute__((noinline)) -void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) -{ - if (!env->ExceptionOccurred()) { - jclass npeClazz = env->FindClass(exc); - env->ThrowNew(npeClazz, msg); - } -} - // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- @@ -444,6 +437,56 @@ static void Surface_unfreezeDisplay( } } +class ScreenshotBitmap : public SkBitmap { +public: + ScreenshotBitmap() { + } + + status_t update(int width, int height) { + status_t res = (width > 0 && height > 0) + ? mScreenshot.update(width, height) + : mScreenshot.update(); + if (res != NO_ERROR) { + return res; + } + + void const* base = mScreenshot.getPixels(); + uint32_t w = mScreenshot.getWidth(); + uint32_t h = mScreenshot.getHeight(); + uint32_t s = mScreenshot.getStride(); + uint32_t f = mScreenshot.getFormat(); + + ssize_t bpr = s * android::bytesPerPixel(f); + setConfig(convertPixelFormat(f), w, h, bpr); + if (f == PIXEL_FORMAT_RGBX_8888) { + setIsOpaque(true); + } + if (w > 0 && h > 0) { + setPixels((void*)base); + } else { + // be safe with an empty bitmap. + setPixels(NULL); + } + + return NO_ERROR; + } + +private: + ScreenshotClient mScreenshot; +}; + +static jobject Surface_screenshot(JNIEnv* env, jobject clazz, jint width, jint height) +{ + ScreenshotBitmap* bitmap = new ScreenshotBitmap(); + + if (bitmap->update(width, height) != NO_ERROR) { + delete bitmap; + return 0; + } + + return GraphicsJNI::createBitmap(env, bitmap, false, NULL); +} + static void Surface_setLayer( JNIEnv* env, jobject clazz, jint zorder) { @@ -669,6 +712,7 @@ static JNINativeMethod gSurfaceMethods[] = { {"setOrientation", "(III)V", (void*)Surface_setOrientation }, {"freezeDisplay", "(I)V", (void*)Surface_freezeDisplay }, {"unfreezeDisplay", "(I)V", (void*)Surface_unfreezeDisplay }, + {"screenshot", "(II)Landroid/graphics/Bitmap;", (void*)Surface_screenshot }, {"setLayer", "(I)V", (void*)Surface_setLayer }, {"setPosition", "(II)V",(void*)Surface_setPosition }, {"setSize", "(II)V",(void*)Surface_setSize }, diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 15b5db4c119f3..61223b3468c11 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -195,6 +195,10 @@ + + true + 90 diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 5c8d88be25c42..f892e9e9293af 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -241,6 +241,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; int mUserRotation = Surface.ROTATION_0; + boolean mAllowAllRotations; boolean mCarDockEnablesAccelerometer; boolean mDeskDockEnablesAccelerometer; int mLidKeyboardAccessibility; @@ -657,6 +658,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_carDockRotation); mDeskDockRotation = readRotation( com.android.internal.R.integer.config_deskDockRotation); + mAllowAllRotations = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_allowAllRotations); mCarDockEnablesAccelerometer = mContext.getResources().getBoolean( com.android.internal.R.bool.config_carDockEnablesAccelerometer); mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean( @@ -2344,7 +2347,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return getCurrentPortraitRotation(lastRotation); } - mOrientationListener.setAllow180Rotation( + mOrientationListener.setAllow180Rotation(mAllowAllRotations || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); // case for nosensor meaning ignore sensor and consider only lid diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java new file mode 100644 index 0000000000000..299567a9163a2 --- /dev/null +++ b/services/java/com/android/server/ScreenRotationAnimation.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010 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; // TODO: use com.android.server.wm, once things move there + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceSession; + +class ScreenRotationAnimation { + private static final String TAG = "ScreenRotationAnimation"; + + Surface mSurface; + int mWidth, mHeight; + + int mBaseRotation; + int mCurRotation; + int mDeltaRotation; + + final Matrix mMatrix = new Matrix(); + final float[] mTmpFloats = new float[9]; + + public ScreenRotationAnimation(Display display, SurfaceSession session) { + final DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); + + Bitmap screenshot = Surface.screenshot(0, 0); + + if (screenshot != null) { + // Screenshot does NOT include rotation! + mBaseRotation = 0; + mWidth = screenshot.getWidth(); + mHeight = screenshot.getHeight(); + } else { + // Just in case. + mBaseRotation = display.getRotation(); + mWidth = dm.widthPixels; + mHeight = dm.heightPixels; + } + + Surface.openTransaction(); + if (mSurface != null) { + mSurface.destroy(); + mSurface = null; + } + try { + mSurface = new Surface(session, 0, "FreezeSurface", + -1, mWidth, mHeight, PixelFormat.OPAQUE, 0); + } catch (Surface.OutOfResourcesException e) { + Slog.w(TAG, "Unable to allocate freeze surface", e); + } + mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 200); + setRotation(display.getRotation()); + + Rect dirty = new Rect(0, 0, mWidth, mHeight); + Canvas c = null; + try { + c = mSurface.lockCanvas(dirty); + } catch (IllegalArgumentException e) { + Slog.w(TAG, "Unable to lock surface", e); + return; + } catch (Surface.OutOfResourcesException e) { + Slog.w(TAG, "Unable to lock surface", e); + return; + } + if (c == null) { + Slog.w(TAG, "Null surface"); + return; + } + + if (screenshot != null) { + c.drawBitmap(screenshot, 0, 0, new Paint(0)); + } else { + c.drawColor(Color.GREEN); + } + + mSurface.unlockCanvasAndPost(c); + Surface.closeTransaction(); + + screenshot.recycle(); + } + + // Must be called while in a transaction. + public void setRotation(int rotation) { + mCurRotation = rotation; + int delta = mCurRotation - mBaseRotation; + if (delta < 0) delta += 4; + mDeltaRotation = delta; + + switch (delta) { + case Surface.ROTATION_0: + mMatrix.reset(); + break; + case Surface.ROTATION_90: + mMatrix.setRotate(90, 0, 0); + mMatrix.postTranslate(0, mWidth); + break; + case Surface.ROTATION_180: + mMatrix.setRotate(180, 0, 0); + mMatrix.postTranslate(mWidth, mHeight); + break; + case Surface.ROTATION_270: + mMatrix.setRotate(270, 0, 0); + mMatrix.postTranslate(mHeight, 0); + break; + } + + mMatrix.getValues(mTmpFloats); + mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X], + (int)mTmpFloats[Matrix.MTRANS_Y]); + mSurface.setMatrix( + mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_X], + mTmpFloats[Matrix.MSKEW_Y], mTmpFloats[Matrix.MSCALE_Y]); + + if (false) { + float[] srcPnts = new float[] { 0, 0, mWidth, mHeight }; + float[] dstPnts = new float[8]; + mMatrix.mapPoints(dstPnts, srcPnts); + Slog.i(TAG, "**** ROTATION: " + delta); + Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1] + + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")"); + Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1] + + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")"); + } + } + + public void dismiss() { + mSurface.destroy(); + } +} diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index b3ea836bfc863..5c32c38009a5d 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -201,6 +201,12 @@ public class WindowManagerService extends IWindowManager.Stub */ static final int DEFAULT_FADE_IN_OUT_DURATION = 400; + /** + * If true, the window manager will do its own custom freezing and general + * management of the screen during rotation. + */ + static final boolean CUSTOM_SCREEN_ROTATION = true; + // Maximum number of milliseconds to wait for input event injection. // FIXME is this value reasonable? private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; @@ -374,6 +380,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mBlurShown; Watermark mWatermark; StrictModeFlash mStrictModeFlash; + ScreenRotationAnimation mScreenRotationAnimation; int mTransactionSequence = 0; @@ -4998,7 +5005,18 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); mInputManager.setDisplayOrientation(0, rotation); if (mDisplayEnabled) { - Surface.setOrientation(0, rotation, animFlags); + if (CUSTOM_SCREEN_ROTATION) { + Surface.freezeDisplay(0); + Surface.openTransaction(); + if (mScreenRotationAnimation != null) { + mScreenRotationAnimation.setRotation(rotation); + } + Surface.closeTransaction(); + Surface.setOrientation(0, rotation, animFlags); + Surface.unfreezeDisplay(0); + } else { + Surface.setOrientation(0, rotation, animFlags); + } } for (int i=mWindows.size()-1; i>=0; i--) { WindowState w = mWindows.get(i); @@ -10817,7 +10835,14 @@ public class WindowManagerService extends IWindowManager.Stub File file = new File("/data/system/frozen"); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); } - Surface.freezeDisplay(0); + + if (CUSTOM_SCREEN_ROTATION) { + if (mScreenRotationAnimation == null) { + mScreenRotationAnimation = new ScreenRotationAnimation(mDisplay, mFxSession); + } + } else { + Surface.freezeDisplay(0); + } } private void stopFreezingDisplayLocked() { @@ -10834,7 +10859,15 @@ public class WindowManagerService extends IWindowManager.Stub if (PROFILE_ORIENTATION) { Debug.stopMethodTracing(); } - Surface.unfreezeDisplay(0); + + if (CUSTOM_SCREEN_ROTATION) { + if (mScreenRotationAnimation != null) { + mScreenRotationAnimation.dismiss(); + mScreenRotationAnimation = null; + } + } else { + Surface.unfreezeDisplay(0); + } mInputMonitor.thawInputDispatchingLw();