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

Commit 1785587c authored by Miranda Kephart's avatar Miranda Kephart Committed by Android (Google) Code Review
Browse files

Merge "Rearrange/rename GlobalScreenshot methods" into rvc-qpr-dev

parents c4919e09 816abfc8
Loading
Loading
Loading
Loading
+218 −227
Original line number Diff line number Diff line
@@ -214,9 +214,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
    private Animator mScreenshotAnimation;
    private Runnable mOnCompleteRunnable;
    private Animator mDismissAnimation;
    private boolean mInDarkMode = false;
    private boolean mDirectionLTR = true;
    private boolean mOrientationPortrait = true;
    private boolean mInDarkMode;
    private boolean mDirectionLTR;
    private boolean mOrientationPortrait;

    private float mCornerSizeX;
    private float mDismissDeltaY;
@@ -245,9 +245,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        }
    };

    /**
     * @param context everything needs a context :(
     */
    @Inject
    public GlobalScreenshot(
            Context context, @Main Resources resources,
@@ -320,6 +317,104 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        inoutInfo.touchableRegion.set(touchRegion);
    }

    void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
        mOnCompleteRunnable = onComplete;

        mDisplay.getRealMetrics(mDisplayMetrics);
        takeScreenshotInternal(
                finisher,
                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
    }

    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
            Consumer<Uri> finisher, Runnable onComplete) {
        // TODO: use task Id, userId, topComponent for smart handler

        mOnCompleteRunnable = onComplete;
        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
            saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
        } else {
            saveScreenshot(screenshot, finisher,
                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
                    true);
        }
    }

    /**
     * Displays a screenshot selector
     */
    @SuppressLint("ClickableViewAccessibility")
    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
        dismissScreenshot("new screenshot requested", true);
        mOnCompleteRunnable = onComplete;

        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
        mScreenshotSelectorView.setOnTouchListener((v, 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(() -> takeScreenshotInternal(finisher, rect));
                        }
                    }

                    view.stopSelection();
                    return true;
            }

            return false;
        });
        mScreenshotLayout.post(() -> {
            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();
        }
    }

    /**
     * Clears current screenshot
     */
    void dismissScreenshot(String reason, boolean immediate) {
        Log.v(TAG, "clearing screenshot: " + reason);
        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
        mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        if (!immediate) {
            mDismissAnimation = createScreenshotDismissAnimation();
            mDismissAnimation.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    clearScreenshot();
                }
            });
            mDismissAnimation.start();
        } else {
            clearScreenshot();
        }
    }

    private void onConfigChanged(Configuration newConfig) {
        boolean needsUpdate = false;
        // dark mode
@@ -408,15 +503,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
            }
            return mScreenshotLayout.onApplyWindowInsets(insets);
        });
        mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
        mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                dismissScreenshot("back pressed", true);
                return true;
            }
            return false;
            }
        });
        // Get focus so that the key events go to the layout.
        mScreenshotLayout.setFocusableInTouchMode(true);
@@ -470,60 +562,20 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        }
    }

    /**
     * Updates the window focusability.  If the window is already showing, then it updates the
     * window immediately, otherwise the layout params will be applied when the window is next
     * shown.
     */
    private void setWindowFocusable(boolean focusable) {
        if (focusable) {
            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
        } else {
            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
        }
        if (mScreenshotLayout.isAttachedToWindow()) {
            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
        }
    }

    /**
     * Creates a new worker thread and saves the screenshot to the media store.
     */
    private void saveScreenshotInWorkerThread(
            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
        data.image = mScreenBitmap;
        data.finisher = finisher;
        data.mActionsReadyListener = actionsReadyListener;

        if (mSaveInBgTask != null) {
            // just log success/failure for the pre-existing screenshot
            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
                @Override
                void onActionsReady(SavedImageData imageData) {
                    logSuccessOnActionsReady(imageData);
                }
            });
        }

        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
        mSaveInBgTask.execute();
    }

    /**
     * Takes a screenshot of the current display and shows an animation.
     */
    private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
    private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
        // copy the input Rect, since SurfaceControl.screenshot can mutate it
        Rect screenRect = new Rect(crop);
        int rot = mDisplay.getRotation();
        int width = crop.width();
        int height = crop.height();
        takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
        saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
                Insets.NONE, true);
    }

    private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
            Insets screenInsets, boolean showFlash) {
        dismissScreenshot("new screenshot requested", true);

@@ -561,85 +613,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        startAnimation(finisher, screenRect, screenInsets, showFlash);
    }

    void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
        mOnCompleteRunnable = onComplete;

        mDisplay.getRealMetrics(mDisplayMetrics);
        takeScreenshot(
                finisher,
                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
    }

    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
            Consumer<Uri> finisher, Runnable onComplete) {
        // TODO: use task Id, userId, topComponent for smart handler

        mOnCompleteRunnable = onComplete;
        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
            takeScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
        } else {
            takeScreenshot(screenshot, finisher,
                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
                    true);
        }
    }

    /**
     * Displays a screenshot selector
     */
    @SuppressLint("ClickableViewAccessibility")
    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
        dismissScreenshot("new screenshot requested", true);
        mOnCompleteRunnable = onComplete;

        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(() -> takeScreenshot(finisher, rect));
                            }
                        }

                        view.stopSelection();
                        return true;
                }

                return false;
            }
        });
        mScreenshotLayout.post(() -> {
            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();
        }
    }

    /**
     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
     * failure).
@@ -670,55 +643,78 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        });
    }

    private boolean isUserSetupComplete() {
        return Settings.Secure.getInt(mContext.getContentResolver(),
                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
    }

    /**
     * Clears current screenshot
     * Starts the animation after taking the screenshot
     */
    void dismissScreenshot(String reason, boolean immediate) {
        Log.v(TAG, "clearing screenshot: " + reason);
        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
        mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
        if (!immediate) {
            mDismissAnimation = createScreenshotDismissAnimation();
            mDismissAnimation.addListener(new AnimatorListenerAdapter() {
    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
            boolean showFlash) {

        // If power save is on, show a toast so there is some visual indication that a
        // screenshot has been taken.
        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        if (powerManager.isPowerSaveMode()) {
            Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
        }

        mScreenshotHandler.post(() -> {
            if (!mScreenshotLayout.isAttachedToWindow()) {
                mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
            }
            mScreenshotAnimatedView.setImageDrawable(
                    createScreenDrawable(mScreenBitmap, screenInsets));
            setAnimatedViewSize(screenRect.width(), screenRect.height());
            // Show when the animation starts
            mScreenshotAnimatedView.setVisibility(View.GONE);

            mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
            // make static preview invisible (from gone) so we can query its location on screen
            mScreenshotPreview.setVisibility(View.INVISIBLE);

            mScreenshotHandler.post(() -> {
                mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);

                mScreenshotAnimation =
                        createScreenshotDropInAnimation(screenRect, showFlash);

                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
                    @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    clearScreenshot();
                    void onActionsReady(SavedImageData imageData) {
                        showUiOnActionsReady(imageData);
                    }
                });
            mDismissAnimation.start();
        } else {
            clearScreenshot();
        }

                // Play the shutter sound to notify that we've taken a screenshot
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);

                mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                mScreenshotPreview.buildLayer();
                mScreenshotAnimation.start();
            });
        });
    }

    private void clearScreenshot() {
        if (mScreenshotLayout.isAttachedToWindow()) {
            mWindowManager.removeView(mScreenshotLayout);
    /**
     * Creates a new worker thread and saves the screenshot to the media store.
     */
    private void saveScreenshotInWorkerThread(
            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
        data.image = mScreenBitmap;
        data.finisher = finisher;
        data.mActionsReadyListener = actionsReadyListener;

        if (mSaveInBgTask != null) {
            // just log success/failure for the pre-existing screenshot
            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
                @Override
                void onActionsReady(SavedImageData imageData) {
                    logSuccessOnActionsReady(imageData);
                }
            });
        }

        // Clear any references to the bitmap
        mScreenshotPreview.setImageDrawable(null);
        mScreenshotAnimatedView.setImageDrawable(null);
        mScreenshotAnimatedView.setVisibility(View.GONE);
        mActionsContainerBackground.setVisibility(View.GONE);
        mActionsContainer.setVisibility(View.GONE);
        mBackgroundProtection.setAlpha(0f);
        mDismissButton.setVisibility(View.GONE);
        mScreenshotPreview.setVisibility(View.GONE);
        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
        mScreenshotPreview.setContentDescription(
                mContext.getResources().getString(R.string.screenshot_preview_description));
        mScreenshotLayout.setAlpha(1);
        mDismissButton.setTranslationY(0);
        mActionsContainer.setTranslationY(0);
        mActionsContainerBackground.setTranslationY(0);
        mScreenshotPreview.setTranslationY(0);
        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
        mSaveInBgTask.execute();
    }

    /**
@@ -768,56 +764,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        }
    }

    /**
     * Starts the animation after taking the screenshot
     */
    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
            boolean showFlash) {

        // If power save is on, show a toast so there is some visual indication that a
        // screenshot has been taken.
        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        if (powerManager.isPowerSaveMode()) {
            Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
        }

        mScreenshotHandler.post(() -> {
            if (!mScreenshotLayout.isAttachedToWindow()) {
                mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
            }
            mScreenshotAnimatedView.setImageDrawable(
                    createScreenDrawable(mScreenBitmap, screenInsets));
            setAnimatedViewSize(screenRect.width(), screenRect.height());
            // Show when the animation starts
            mScreenshotAnimatedView.setVisibility(View.GONE);

            mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
            // make static preview invisible (from gone) so we can query its location on screen
            mScreenshotPreview.setVisibility(View.INVISIBLE);

            mScreenshotHandler.post(() -> {
                mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);

                mScreenshotAnimation =
                        createScreenshotDropInAnimation(screenRect, showFlash);

                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
                    @Override
                    void onActionsReady(SavedImageData imageData) {
                        showUiOnActionsReady(imageData);
                    }
                });

                // Play the shutter sound to notify that we've taken a screenshot
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);

                mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                mScreenshotPreview.buildLayer();
                mScreenshotAnimation.start();
            });
        });
    }

    private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
        Rect previewBounds = new Rect();
        mScreenshotPreview.getBoundsOnScreen(previewBounds);
@@ -1070,6 +1016,30 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        return animSet;
    }

    private void clearScreenshot() {
        if (mScreenshotLayout.isAttachedToWindow()) {
            mWindowManager.removeView(mScreenshotLayout);
        }

        // Clear any references to the bitmap
        mScreenshotPreview.setImageDrawable(null);
        mScreenshotAnimatedView.setImageDrawable(null);
        mScreenshotAnimatedView.setVisibility(View.GONE);
        mActionsContainerBackground.setVisibility(View.GONE);
        mActionsContainer.setVisibility(View.GONE);
        mBackgroundProtection.setAlpha(0f);
        mDismissButton.setVisibility(View.GONE);
        mScreenshotPreview.setVisibility(View.GONE);
        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
        mScreenshotPreview.setContentDescription(
                mContext.getResources().getString(R.string.screenshot_preview_description));
        mScreenshotLayout.setAlpha(1);
        mDismissButton.setTranslationY(0);
        mActionsContainer.setTranslationY(0);
        mActionsContainerBackground.setTranslationY(0);
        mScreenshotPreview.setTranslationY(0);
    }

    private void setAnimatedViewSize(int width, int height) {
        ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
        layoutParams.width = width;
@@ -1077,6 +1047,27 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
        mScreenshotAnimatedView.setLayoutParams(layoutParams);
    }

    /**
     * Updates the window focusability.  If the window is already showing, then it updates the
     * window immediately, otherwise the layout params will be applied when the window is next
     * shown.
     */
    private void setWindowFocusable(boolean focusable) {
        if (focusable) {
            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
        } else {
            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
        }
        if (mScreenshotLayout.isAttachedToWindow()) {
            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
        }
    }

    private boolean isUserSetupComplete() {
        return Settings.Secure.getInt(mContext.getContentResolver(),
                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
    }

    /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
    private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
        int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
+1 −1
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ public class TakeScreenshotService extends Service {

            switch (msg.what) {
                case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
                    mScreenshot.takeScreenshot(uriConsumer, onComplete);
                    mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
                    break;
                case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
                    mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);