Loading core/java/android/view/WindowManager.java +20 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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. */ Loading packages/SystemUI/res/layout/global_screenshot.xml +6 −0 Original line number Diff line number Diff line Loading @@ -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> packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +81 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -407,6 +408,7 @@ class GlobalScreenshot { private Bitmap mScreenBitmap; private View mScreenshotLayout; private ScreenshotSelectorView mScreenshotSelectorView; private ImageView mBackgroundView; private ImageView mScreenshotView; private ImageView mScreenshotFlash; Loading Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java 0 → 100644 +82 −0 Original line number Diff line number Diff line /* * 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); } } } packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +28 −14 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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); } mScreenshot.takeScreenshot(new Runnable() { @Override public void run() { Runnable finisher = 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; } } }; Loading @@ -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; } } Loading
core/java/android/view/WindowManager.java +20 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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; Loading Loading @@ -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. */ Loading
packages/SystemUI/res/layout/global_screenshot.xml +6 −0 Original line number Diff line number Diff line Loading @@ -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>
packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +81 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -407,6 +408,7 @@ class GlobalScreenshot { private Bitmap mScreenBitmap; private View mScreenshotLayout; private ScreenshotSelectorView mScreenshotSelectorView; private ImageView mBackgroundView; private ImageView mScreenshotView; private ImageView mScreenshotFlash; Loading Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java 0 → 100644 +82 −0 Original line number Diff line number Diff line /* * 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); } } }
packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +28 −14 Original line number Diff line number Diff line Loading @@ -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"; Loading @@ -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); } mScreenshot.takeScreenshot(new Runnable() { @Override public void run() { Runnable finisher = 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; } } }; Loading @@ -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; } }