Merge "Partial screenshot" into nyc-dev
This commit is contained in:
@@ -134,6 +134,18 @@ public interface WindowManager extends ViewManager {
|
||||
void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Message for taking fullscreen screenshot
|
||||
* @hide
|
||||
*/
|
||||
final int TAKE_SCREENSHOT_FULLSCREEN = 1;
|
||||
|
||||
/**
|
||||
* Message for taking screenshot of selected region.
|
||||
* @hide
|
||||
*/
|
||||
final int TAKE_SCREENSHOT_SELECTED_REGION = 2;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -269,6 +281,7 @@ public interface WindowManager extends ViewManager {
|
||||
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"),
|
||||
@ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, to = "TYPE_DOCK_DIVIDER"),
|
||||
@ViewDebug.IntToString(from = TYPE_QS_DIALOG, to = "TYPE_QS_DIALOG"),
|
||||
@ViewDebug.IntToString(from = TYPE_SCREENSHOT, to = "TYPE_SCREENSHOT")
|
||||
})
|
||||
public int type;
|
||||
|
||||
@@ -621,6 +634,13 @@ public interface WindowManager extends ViewManager {
|
||||
*/
|
||||
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
|
||||
|
||||
/**
|
||||
* Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is
|
||||
* reserved for screenshot region selection.
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
|
||||
|
||||
/**
|
||||
* End of types of system windows.
|
||||
*/
|
||||
|
||||
@@ -33,4 +33,10 @@
|
||||
android:layout_height="match_parent"
|
||||
android:src="@android:color/white"
|
||||
android:visibility="gone" />
|
||||
<com.android.systemui.screenshot.ScreenshotSelectorView
|
||||
android:id="@+id/global_screenshot_selector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:pointerShape="crosshair"/>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaActionSound;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
@@ -407,6 +408,7 @@ class GlobalScreenshot {
|
||||
|
||||
private Bitmap mScreenBitmap;
|
||||
private View mScreenshotLayout;
|
||||
private ScreenshotSelectorView mScreenshotSelectorView;
|
||||
private ImageView mBackgroundView;
|
||||
private ImageView mScreenshotView;
|
||||
private ImageView mScreenshotFlash;
|
||||
@@ -437,7 +439,11 @@ class GlobalScreenshot {
|
||||
mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
|
||||
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
|
||||
mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
|
||||
mScreenshotSelectorView = (ScreenshotSelectorView) mScreenshotLayout.findViewById(
|
||||
R.id.global_screenshot_selector);
|
||||
mScreenshotLayout.setFocusable(true);
|
||||
mScreenshotSelectorView.setFocusable(true);
|
||||
mScreenshotSelectorView.setFocusableInTouchMode(true);
|
||||
mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
@@ -449,7 +455,7 @@ class GlobalScreenshot {
|
||||
// Setup the window that we are going to use
|
||||
mWindowLayoutParams = new WindowManager.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
|
||||
WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
|
||||
WindowManager.LayoutParams.TYPE_SCREENSHOT,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
@@ -525,7 +531,8 @@ class GlobalScreenshot {
|
||||
/**
|
||||
* Takes a screenshot of the current display and shows an animation.
|
||||
*/
|
||||
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
|
||||
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
|
||||
int x, int y, int width, int height) {
|
||||
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots
|
||||
// only in the natural orientation of the device :!)
|
||||
mDisplay.getRealMetrics(mDisplayMetrics);
|
||||
@@ -565,6 +572,13 @@ class GlobalScreenshot {
|
||||
mScreenBitmap = ss;
|
||||
}
|
||||
|
||||
if (width != mDisplayMetrics.widthPixels || height != mDisplayMetrics.heightPixels) {
|
||||
// Crop the screenshot to selected region
|
||||
Bitmap cropped = Bitmap.createBitmap(mScreenBitmap, x, y, width, height);
|
||||
mScreenBitmap.recycle();
|
||||
mScreenBitmap = cropped;
|
||||
}
|
||||
|
||||
// Optimizations
|
||||
mScreenBitmap.setHasAlpha(false);
|
||||
mScreenBitmap.prepareToDraw();
|
||||
@@ -574,6 +588,71 @@ class GlobalScreenshot {
|
||||
statusBarVisible, navBarVisible);
|
||||
}
|
||||
|
||||
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
|
||||
mDisplay.getRealMetrics(mDisplayMetrics);
|
||||
takeScreenshot(finisher, statusBarVisible, navBarVisible, 0, 0, mDisplayMetrics.widthPixels,
|
||||
mDisplayMetrics.heightPixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a screenshot selector
|
||||
*/
|
||||
void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible,
|
||||
final boolean navBarVisible) {
|
||||
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
|
||||
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
ScreenshotSelectorView view = (ScreenshotSelectorView) v;
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
view.startSelection((int) event.getX(), (int) event.getY());
|
||||
return true;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
view.updateSelection((int) event.getX(), (int) event.getY());
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
view.setVisibility(View.GONE);
|
||||
mWindowManager.removeView(mScreenshotLayout);
|
||||
final Rect rect = view.getSelectionRect();
|
||||
if (rect != null) {
|
||||
if (rect.width() != 0 && rect.height() != 0) {
|
||||
// Need mScreenshotLayout to handle it after the view disappears
|
||||
mScreenshotLayout.post(new Runnable() {
|
||||
public void run() {
|
||||
takeScreenshot(finisher, statusBarVisible, navBarVisible,
|
||||
rect.left, rect.top, rect.width(), rect.height());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
view.stopSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
mScreenshotLayout.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mScreenshotSelectorView.setVisibility(View.VISIBLE);
|
||||
mScreenshotSelectorView.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels screenshot request
|
||||
*/
|
||||
void stopScreenshot() {
|
||||
// If the selector layer still presents on screen, we remove it and resets its state.
|
||||
if (mScreenshotSelectorView.getSelectionRect() != null) {
|
||||
mWindowManager.removeView(mScreenshotLayout);
|
||||
mScreenshotSelectorView.stopSelection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation after taking the screenshot
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.screenshot;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Draws a selection rectangle while taking screenshot
|
||||
*/
|
||||
public class ScreenshotSelectorView extends View {
|
||||
private Point mStartPoint;
|
||||
private Rect mSelectionRect;
|
||||
private final Paint mPaintSelection, mPaintBackground;
|
||||
|
||||
public ScreenshotSelectorView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ScreenshotSelectorView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mPaintBackground = new Paint(Color.BLACK);
|
||||
mPaintBackground.setAlpha(160);
|
||||
mPaintSelection = new Paint(Color.TRANSPARENT);
|
||||
mPaintSelection.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
}
|
||||
|
||||
public void startSelection(int x, int y) {
|
||||
mStartPoint = new Point(x, y);
|
||||
mSelectionRect = new Rect(x, y, x, y);
|
||||
}
|
||||
|
||||
public void updateSelection(int x, int y) {
|
||||
if (mSelectionRect != null) {
|
||||
mSelectionRect.left = Math.min(mStartPoint.x, x);
|
||||
mSelectionRect.right = Math.max(mStartPoint.x, x);
|
||||
mSelectionRect.top = Math.min(mStartPoint.y, y);
|
||||
mSelectionRect.bottom = Math.max(mStartPoint.y, y);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public Rect getSelectionRect() {
|
||||
return mSelectionRect;
|
||||
}
|
||||
|
||||
public void stopSelection() {
|
||||
mStartPoint = null;
|
||||
mSelectionRect = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
|
||||
if (mSelectionRect != null) {
|
||||
canvas.drawRect(mSelectionRect, mPaintSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.view.WindowManager;
|
||||
|
||||
public class TakeScreenshotService extends Service {
|
||||
private static final String TAG = "TakeScreenshotService";
|
||||
@@ -32,21 +33,28 @@ public class TakeScreenshotService extends Service {
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case 1:
|
||||
final Messenger callback = msg.replyTo;
|
||||
if (mScreenshot == null) {
|
||||
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
|
||||
final Messenger callback = msg.replyTo;
|
||||
Runnable finisher = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Message reply = Message.obtain(null, 1);
|
||||
try {
|
||||
callback.send(reply);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
mScreenshot.takeScreenshot(new Runnable() {
|
||||
@Override public void run() {
|
||||
Message reply = Message.obtain(null, 1);
|
||||
try {
|
||||
callback.send(reply);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}, msg.arg1 > 0, msg.arg2 > 0);
|
||||
}
|
||||
};
|
||||
if (mScreenshot == null) {
|
||||
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
|
||||
}
|
||||
|
||||
switch (msg.what) {
|
||||
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
|
||||
mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
|
||||
break;
|
||||
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
|
||||
mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -55,4 +63,10 @@ public class TakeScreenshotService extends Service {
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new Messenger(mHandler).getBinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
if (mScreenshot != null) mScreenshot.stopScreenshot();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3436,7 +3436,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|
||||
case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
|
||||
case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
|
||||
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
|
||||
case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
|
||||
case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
|
||||
case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
|
||||
return AccessibilityWindowInfo.TYPE_SYSTEM;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
|
||||
import static android.view.WindowManager.DOCKED_TOP;
|
||||
import static android.view.WindowManager.DOCKED_LEFT;
|
||||
import static android.view.WindowManager.DOCKED_RIGHT;
|
||||
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
|
||||
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
|
||||
import static android.view.WindowManager.LayoutParams.*;
|
||||
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
|
||||
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
|
||||
@@ -1239,7 +1241,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
+ SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
|
||||
mScreenshotChordVolumeDownKeyConsumed = true;
|
||||
cancelPendingPowerKeyAction();
|
||||
|
||||
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
|
||||
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
|
||||
}
|
||||
}
|
||||
@@ -1269,12 +1271,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mScreenshotRunnable = new Runnable() {
|
||||
private class ScreenshotRunnable implements Runnable {
|
||||
private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
|
||||
|
||||
public void setScreenshotType(int screenshotType) {
|
||||
mScreenshotType = screenshotType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
takeScreenshot();
|
||||
takeScreenshot(mScreenshotType);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();
|
||||
|
||||
@Override
|
||||
public void showGlobalActions() {
|
||||
@@ -2304,29 +2314,33 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
case TYPE_NAVIGATION_BAR_PANEL:
|
||||
// some panels (e.g. search) need to show on top of the navigation bar
|
||||
return 22;
|
||||
case TYPE_SCREENSHOT:
|
||||
// screenshot selection layer shouldn't go above system error, but it should cover
|
||||
// navigation bars at the very least.
|
||||
return 23;
|
||||
case TYPE_SYSTEM_ERROR:
|
||||
// system-level error dialogs
|
||||
return 23;
|
||||
return 24;
|
||||
case TYPE_MAGNIFICATION_OVERLAY:
|
||||
// used to highlight the magnified portion of a display
|
||||
return 24;
|
||||
return 25;
|
||||
case TYPE_DISPLAY_OVERLAY:
|
||||
// used to simulate secondary display devices
|
||||
return 25;
|
||||
return 26;
|
||||
case TYPE_DRAG:
|
||||
// the drag layer: input for drag-and-drop is associated with this window,
|
||||
// which sits above all other focusable windows
|
||||
return 26;
|
||||
return 27;
|
||||
case TYPE_ACCESSIBILITY_OVERLAY:
|
||||
// overlay put by accessibility services to intercept user interaction
|
||||
return 27;
|
||||
case TYPE_SECURE_SYSTEM_OVERLAY:
|
||||
return 28;
|
||||
case TYPE_BOOT_PROGRESS:
|
||||
case TYPE_SECURE_SYSTEM_OVERLAY:
|
||||
return 29;
|
||||
case TYPE_BOOT_PROGRESS:
|
||||
return 30;
|
||||
case TYPE_POINTER:
|
||||
// the (mouse) pointer layer
|
||||
return 30;
|
||||
return 31;
|
||||
}
|
||||
Log.e(TAG, "Unknown window type: " + type);
|
||||
return 2;
|
||||
@@ -3026,6 +3040,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed()
|
||||
&& event.isCtrlPressed()) {
|
||||
if (down && repeatCount == 0) {
|
||||
int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
|
||||
: TAKE_SCREENSHOT_FULLSCREEN;
|
||||
mScreenshotRunnable.setScreenshotType(type);
|
||||
mHandler.post(mScreenshotRunnable);
|
||||
return -1;
|
||||
}
|
||||
} else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
|
||||
if (down) {
|
||||
if (repeatCount == 0) {
|
||||
@@ -3073,6 +3096,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
} else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
|
||||
if (down && repeatCount == 0) {
|
||||
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
|
||||
mHandler.post(mScreenshotRunnable);
|
||||
}
|
||||
return -1;
|
||||
@@ -4402,9 +4426,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
"Laying out navigation bar window: (%d,%d - %d,%d)",
|
||||
pf.left, pf.top, pf.right, pf.bottom));
|
||||
} else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
|
||||
|| attrs.type == TYPE_BOOT_PROGRESS)
|
||||
|| attrs.type == TYPE_BOOT_PROGRESS
|
||||
|| attrs.type == TYPE_SCREENSHOT)
|
||||
&& ((fl & FLAG_FULLSCREEN) != 0)) {
|
||||
// Fullscreen secure system overlays get what they ask for.
|
||||
// Fullscreen secure system overlays get what they ask for. Screenshot region
|
||||
// selection overlay should also expand to full screen.
|
||||
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
|
||||
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
|
||||
pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
|
||||
@@ -5159,7 +5185,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
};
|
||||
|
||||
// Assume this is called from the Handler thread.
|
||||
private void takeScreenshot() {
|
||||
private void takeScreenshot(final int screenshotType) {
|
||||
synchronized (mScreenshotLock) {
|
||||
if (mScreenshotConnection != null) {
|
||||
return;
|
||||
@@ -5176,7 +5202,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
return;
|
||||
}
|
||||
Messenger messenger = new Messenger(service);
|
||||
Message msg = Message.obtain(null, 1);
|
||||
Message msg = Message.obtain(null, screenshotType);
|
||||
final ServiceConnection myConn = this;
|
||||
Handler h = new Handler(mHandler.getLooper()) {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user