Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ca619fe authored by Muyuan Li's avatar Muyuan Li
Browse files

Partial screenshot

Added a partial screenshot function in TakeScreenshotService
also added corresponding shortcut keys in PhoneWindowManager

Bug: 26820467
Change-Id: Id67cd3b4b0eed848eb4665056766546500bdac88
(cherry picked from commit 03e45541e9d54a2f285906ac7b5bcb374db14495)
parent eff0a83a
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -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.
         */
+6 −0
Original line number Diff line number Diff line
@@ -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>
+81 −2
Original line number Diff line number Diff line
@@ -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
+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);
        }
    }
}
+28 −14
Original line number Diff line number Diff line
@@ -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);
                    }
                    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;
            }
        }
    };
@@ -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