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

Commit 40cc02f9 authored by Miranda Kephart's avatar Miranda Kephart Committed by Matt Casey
Browse files

Abstract out surface between ScreenshotController and ScreenshotView

Building block for allowing us to rewrite the screenshot UI under a
flag.

Bug: 329659738
Flag: NONE
Test: atest, manual

Change-Id: Ib5dceefeda21e24c7eccf68cf9f44a154ae95f90
Merged-In: Ib5dceefeda21e24c7eccf68cf9f44a154ae95f90
parent a20527d1
Loading
Loading
Loading
Loading
+159 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.animation.Animator
import android.app.Notification
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.Display
import android.view.LayoutInflater
import android.view.ScrollCaptureResponse
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowInsets
import android.window.OnBackInvokedDispatcher
import com.android.internal.logging.UiEventLogger
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.res.R

/**
 * Legacy implementation of screenshot view methods. Just proxies the calls down into the original
 * ScreenshotView.
 */
class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
    override val view: ScreenshotView =
        LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView
    override val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener
    override val screenshotPreview: View

    override var defaultDisplay: Int = Display.DEFAULT_DISPLAY
        set(value) {
            view.setDefaultDisplay(value)
        }
    override var defaultTimeoutMillis: Long = 6000
        set(value) {
            view.setDefaultTimeoutMillis(value)
        }
    override var onKeyListener: View.OnKeyListener? = null
        set(value) {
            view.setOnKeyListener(value)
        }
    override var flags: FeatureFlags? = null
        set(value) {
            view.setFlags(value)
        }
    override var packageName: String = ""
        set(value) {
            view.setPackageName(value)
        }
    override var logger: UiEventLogger? = null
        set(value) {
            view.setUiEventLogger(value)
        }
    override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
        set(value) {
            view.setCallbacks(value)
        }
    override var screenshot: ScreenshotData? = null
        set(value) {
            view.setScreenshot(value)
        }

    override val isAttachedToWindow
        get() = view.isAttachedToWindow
    override val isDismissing
        get() = view.isDismissing
    override val isPendingSharedTransition
        get() = view.isPendingSharedTransition

    init {
        internalInsetsListener = view
        screenshotPreview = view.screenshotPreview
    }

    override fun reset() = view.reset()
    override fun updateInsets(insets: WindowInsets) = view.updateInsets(insets)
    override fun updateOrientation(insets: WindowInsets) = view.updateOrientation(insets)

    override fun badgeScreenshot(userBadgedIcon: Drawable) = view.badgeScreenshot(userBadgedIcon)

    override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator =
        view.createScreenshotDropInAnimation(screenRect, showFlash)

    override fun addQuickShareChip(quickShareAction: Notification.Action) =
        view.addQuickShareChip(quickShareAction)

    override fun setChipIntents(imageData: ScreenshotController.SavedImageData) =
        view.setChipIntents(imageData)

    override fun animateDismissal() = view.animateDismissal()

    override fun showScrollChip(packageName: String, onClick: Runnable) =
        view.showScrollChip(packageName, onClick)

    override fun hideScrollChip() = view.hideScrollChip()

    override fun prepareScrollingTransition(
        response: ScrollCaptureResponse,
        screenBitmap: Bitmap,
        newScreenshot: Bitmap,
        screenshotTakenInPortrait: Boolean
    ) =
        view.prepareScrollingTransition(
            response,
            screenBitmap,
            newScreenshot,
            screenshotTakenInPortrait
        )

    override fun startLongScreenshotTransition(
        transitionDestination: Rect,
        onTransitionEnd: Runnable,
        longScreenshot: ScrollCaptureController.LongScreenshot
    ) = view.startLongScreenshotTransition(transitionDestination, onTransitionEnd, longScreenshot)

    override fun restoreNonScrollingUi() = view.restoreNonScrollingUi()

    override fun stopInputListening() = view.stopInputListening()

    override fun requestFocus() {
        view.requestFocus()
    }

    override fun announceForAccessibility(string: String) = view.announceForAccessibility(string)

    override fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener) =
        view.addOnAttachStateChangeListener(listener)

    override fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher? =
        view.findOnBackInvokedDispatcher()

    override fun getViewTreeObserver(): ViewTreeObserver = view.viewTreeObserver

    override fun post(runnable: Runnable) {
        view.post(runnable)
    }

    class Factory : ScreenshotViewProxy.Factory {
        override fun getProxy(context: Context): ScreenshotViewProxy {
            return LegacyScreenshotViewProxy(context)
        }
    }
}
+52 −52
Original line number Original line Diff line number Diff line
@@ -65,7 +65,6 @@ import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
import android.view.ScrollCaptureResponse;
@@ -165,7 +164,7 @@ public class ScreenshotController {
    /**
    /**
     * Structure returned by the SaveImageInBackgroundTask
     * Structure returned by the SaveImageInBackgroundTask
     */
     */
    static class SavedImageData {
    public static class SavedImageData {
        public Uri uri;
        public Uri uri;
        public List<Notification.Action> smartActions;
        public List<Notification.Action> smartActions;
        public Notification.Action quickShareAction;
        public Notification.Action quickShareAction;
@@ -237,6 +236,7 @@ public class ScreenshotController {


    private final WindowContext mContext;
    private final WindowContext mContext;
    private final FeatureFlags mFlags;
    private final FeatureFlags mFlags;
    private final ScreenshotViewProxy mViewProxy;
    private final ScreenshotNotificationsController mNotificationsController;
    private final ScreenshotNotificationsController mNotificationsController;
    private final ScreenshotSmartActions mScreenshotSmartActions;
    private final ScreenshotSmartActions mScreenshotSmartActions;
    private final UiEventLogger mUiEventLogger;
    private final UiEventLogger mUiEventLogger;
@@ -272,7 +272,6 @@ public class ScreenshotController {
        respondToKeyDismissal();
        respondToKeyDismissal();
    };
    };


    private ScreenshotView mScreenshotView;
    private final MessageContainerController mMessageContainerController;
    private final MessageContainerController mMessageContainerController;
    private Bitmap mScreenBitmap;
    private Bitmap mScreenBitmap;
    private SaveImageInBackgroundTask mSaveInBgTask;
    private SaveImageInBackgroundTask mSaveInBgTask;
@@ -305,6 +304,7 @@ public class ScreenshotController {
    ScreenshotController(
    ScreenshotController(
            Context context,
            Context context,
            FeatureFlags flags,
            FeatureFlags flags,
            ScreenshotViewProxy.Factory viewProxyFactory,
            ScreenshotSmartActions screenshotSmartActions,
            ScreenshotSmartActions screenshotSmartActions,
            ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
            ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
            ScrollCaptureClient scrollCaptureClient,
            ScrollCaptureClient scrollCaptureClient,
@@ -360,6 +360,8 @@ public class ScreenshotController {
        mMessageContainerController = messageContainerController;
        mMessageContainerController = messageContainerController;
        mAssistContentRequester = assistContentRequester;
        mAssistContentRequester = assistContentRequester;


        mViewProxy = viewProxyFactory.getProxy(mContext);

        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
        mAccessibilityManager = AccessibilityManager.getInstance(mContext);


        // Setup the window that we are going to use
        // Setup the window that we are going to use
@@ -461,7 +463,7 @@ public class ScreenshotController {


        // The window is focusable by default
        // The window is focusable by default
        setWindowFocusable(true);
        setWindowFocusable(true);
        mScreenshotView.requestFocus();
        mViewProxy.requestFocus();


        enqueueScrollCaptureRequest(screenshot.getUserHandle());
        enqueueScrollCaptureRequest(screenshot.getUserHandle());


@@ -485,10 +487,10 @@ public class ScreenshotController {
            mMessageContainerController.onScreenshotTaken(screenshot);
            mMessageContainerController.onScreenshotTaken(screenshot);
        });
        });


        mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
        mViewProxy.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
                mContext.getDrawable(R.drawable.overlay_badge_background),
                mContext.getDrawable(R.drawable.overlay_badge_background),
                screenshot.getUserHandle()));
                screenshot.getUserHandle()));
        mScreenshotView.setScreenshot(screenshot);
        mViewProxy.setScreenshot(screenshot);


        // ignore system bar insets for the purpose of window layout
        // ignore system bar insets for the purpose of window layout
        mWindow.getDecorView().setOnApplyWindowInsetsListener(
        mWindow.getDecorView().setOnApplyWindowInsetsListener(
@@ -503,31 +505,31 @@ public class ScreenshotController {
    void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
    void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
        withWindowAttached(() -> {
        withWindowAttached(() -> {
            if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
            if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
                mScreenshotView.announceForAccessibility(mContext.getResources().getString(
                mViewProxy.announceForAccessibility(mContext.getResources().getString(
                        R.string.screenshot_saving_work_profile_title));
                        R.string.screenshot_saving_work_profile_title));
            } else {
            } else {
                mScreenshotView.announceForAccessibility(
                mViewProxy.announceForAccessibility(
                        mContext.getResources().getString(R.string.screenshot_saving_title));
                        mContext.getResources().getString(R.string.screenshot_saving_title));
            }
            }
        });
        });


        mScreenshotView.reset();
        mViewProxy.reset();


        if (mScreenshotView.isAttachedToWindow()) {
        if (mViewProxy.isAttachedToWindow()) {
            // if we didn't already dismiss for another reason
            // if we didn't already dismiss for another reason
            if (!mScreenshotView.isDismissing()) {
            if (!mViewProxy.isDismissing()) {
                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
                        oldPackageName);
                        oldPackageName);
            }
            }
            if (DEBUG_WINDOW) {
            if (DEBUG_WINDOW) {
                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
                        + "(dismissing=" + mScreenshotView.isDismissing() + ")");
                        + "(dismissing=" + mViewProxy.isDismissing() + ")");
            }
            }
        }
        }


        mScreenshotView.setPackageName(mPackageName);
        mViewProxy.setPackageName(mPackageName);


        mScreenshotView.updateOrientation(
        mViewProxy.updateOrientation(
                mWindowManager.getCurrentWindowMetrics().getWindowInsets());
                mWindowManager.getCurrentWindowMetrics().getWindowInsets());
    }
    }


@@ -539,7 +541,7 @@ public class ScreenshotController {
            Log.d(TAG, "dismissScreenshot");
            Log.d(TAG, "dismissScreenshot");
        }
        }
        // If we're already animating out, don't restart the animation
        // If we're already animating out, don't restart the animation
        if (mScreenshotView.isDismissing()) {
        if (mViewProxy.isDismissing()) {
            if (DEBUG_DISMISS) {
            if (DEBUG_DISMISS) {
                Log.v(TAG, "Already dismissing, ignoring duplicate command");
                Log.v(TAG, "Already dismissing, ignoring duplicate command");
            }
            }
@@ -547,11 +549,11 @@ public class ScreenshotController {
        }
        }
        mUiEventLogger.log(event, 0, mPackageName);
        mUiEventLogger.log(event, 0, mPackageName);
        mScreenshotHandler.cancelTimeout();
        mScreenshotHandler.cancelTimeout();
        mScreenshotView.animateDismissal();
        mViewProxy.animateDismissal();
    }
    }


    boolean isPendingSharedTransition() {
    boolean isPendingSharedTransition() {
        return mScreenshotView.isPendingSharedTransition();
        return mViewProxy.isPendingSharedTransition();
    }
    }


    // Any cleanup needed when the service is being destroyed.
    // Any cleanup needed when the service is being destroyed.
@@ -591,18 +593,15 @@ public class ScreenshotController {
            Log.d(TAG, "reloadAssets()");
            Log.d(TAG, "reloadAssets()");
        }
        }


        // Inflate the screenshot layout
        mMessageContainerController.setView(mViewProxy.getView());
        mScreenshotView = (ScreenshotView)
        mViewProxy.addOnAttachStateChangeListener(
                LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
        mMessageContainerController.setView(mScreenshotView);
        mScreenshotView.addOnAttachStateChangeListener(
                new View.OnAttachStateChangeListener() {
                new View.OnAttachStateChangeListener() {
                    @Override
                    @Override
                    public void onViewAttachedToWindow(@NonNull View v) {
                    public void onViewAttachedToWindow(@NonNull View v) {
                        if (DEBUG_INPUT) {
                        if (DEBUG_INPUT) {
                            Log.d(TAG, "Registering Predictive Back callback");
                            Log.d(TAG, "Registering Predictive Back callback");
                        }
                        }
                        mScreenshotView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
                        mViewProxy.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
                                OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
                                OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
                    }
                    }


@@ -611,11 +610,12 @@ public class ScreenshotController {
                        if (DEBUG_INPUT) {
                        if (DEBUG_INPUT) {
                            Log.d(TAG, "Unregistering Predictive Back callback");
                            Log.d(TAG, "Unregistering Predictive Back callback");
                        }
                        }
                        mScreenshotView.findOnBackInvokedDispatcher()
                        mViewProxy.findOnBackInvokedDispatcher()
                                .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
                                .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
                    }
                    }
                });
                });
        mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
        mViewProxy.setLogger(mUiEventLogger);
        mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() {
            @Override
            @Override
            public void onUserInteraction() {
            public void onUserInteraction() {
                if (DEBUG_INPUT) {
                if (DEBUG_INPUT) {
@@ -640,11 +640,12 @@ public class ScreenshotController {
                // TODO(159460485): Remove this when focus is handled properly in the system
                // TODO(159460485): Remove this when focus is handled properly in the system
                setWindowFocusable(false);
                setWindowFocusable(false);
            }
            }
        }, mFlags);
        });
        mScreenshotView.setDefaultDisplay(mDisplayId);
        mViewProxy.setFlags(mFlags);
        mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
        mViewProxy.setDefaultDisplay(mDisplayId);
        mViewProxy.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());


        mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
        mViewProxy.setOnKeyListener((v, keyCode, event) -> {
            if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
            if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
                if (DEBUG_INPUT) {
                if (DEBUG_INPUT) {
                    Log.d(TAG, "onKeyEvent: " + keyCode);
                    Log.d(TAG, "onKeyEvent: " + keyCode);
@@ -658,23 +659,24 @@ public class ScreenshotController {
        if (DEBUG_WINDOW) {
        if (DEBUG_WINDOW) {
            Log.d(TAG, "adding OnComputeInternalInsetsListener");
            Log.d(TAG, "adding OnComputeInternalInsetsListener");
        }
        }
        mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
        mViewProxy.getViewTreeObserver().addOnComputeInternalInsetsListener(
                mViewProxy.getInternalInsetsListener());
        if (DEBUG_WINDOW) {
        if (DEBUG_WINDOW) {
            Log.d(TAG, "setContentView: " + mScreenshotView);
            Log.d(TAG, "setContentView: " + mViewProxy.getView());
        }
        }
        setContentView(mScreenshotView);
        setContentView(mViewProxy.getView());
    }
    }


    private void prepareAnimation(Rect screenRect, boolean showFlash,
    private void prepareAnimation(Rect screenRect, boolean showFlash,
            Runnable onAnimationComplete) {
            Runnable onAnimationComplete) {
        mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
        mViewProxy.getViewTreeObserver().addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    @Override
                    public boolean onPreDraw() {
                    public boolean onPreDraw() {
                        if (DEBUG_WINDOW) {
                        if (DEBUG_WINDOW) {
                            Log.d(TAG, "onPreDraw: startAnimation");
                            Log.d(TAG, "onPreDraw: startAnimation");
                        }
                        }
                        mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
                        mViewProxy.getViewTreeObserver().removeOnPreDrawListener(this);
                        startAnimation(screenRect, showFlash, onAnimationComplete);
                        startAnimation(screenRect, showFlash, onAnimationComplete);
                        return true;
                        return true;
                    }
                    }
@@ -694,13 +696,13 @@ public class ScreenshotController {
                            if (mConfigChanges.applyNewConfig(mContext.getResources())) {
                            if (mConfigChanges.applyNewConfig(mContext.getResources())) {
                                // Hide the scroll chip until we know it's available in this
                                // Hide the scroll chip until we know it's available in this
                                // orientation
                                // orientation
                                mScreenshotView.hideScrollChip();
                                mViewProxy.hideScrollChip();
                                // Delay scroll capture eval a bit to allow the underlying activity
                                // Delay scroll capture eval a bit to allow the underlying activity
                                // to set up in the new orientation.
                                // to set up in the new orientation.
                                mScreenshotHandler.postDelayed(() -> {
                                mScreenshotHandler.postDelayed(() -> {
                                    requestScrollCapture(owner);
                                    requestScrollCapture(owner);
                                }, 150);
                                }, 150);
                                mScreenshotView.updateInsets(
                                mViewProxy.updateInsets(
                                        mWindowManager.getCurrentWindowMetrics().getWindowInsets());
                                        mWindowManager.getCurrentWindowMetrics().getWindowInsets());
                                // Screenshot animation calculations won't be valid anymore,
                                // Screenshot animation calculations won't be valid anymore,
                                // so just end
                                // so just end
@@ -759,16 +761,16 @@ public class ScreenshotController {
                    + mLastScrollCaptureResponse.getWindowTitle() + "]");
                    + mLastScrollCaptureResponse.getWindowTitle() + "]");


            final ScrollCaptureResponse response = mLastScrollCaptureResponse;
            final ScrollCaptureResponse response = mLastScrollCaptureResponse;
            mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
            mViewProxy.showScrollChip(response.getPackageName(), /* onClick */ () -> {
                DisplayMetrics displayMetrics = new DisplayMetrics();
                DisplayMetrics displayMetrics = new DisplayMetrics();
                getDisplay().getRealMetrics(displayMetrics);
                getDisplay().getRealMetrics(displayMetrics);
                Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId,
                Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId,
                        new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
                        new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));


                mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
                mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
                        mScreenshotTakenInPortrait);
                        mScreenshotTakenInPortrait);
                // delay starting scroll capture to make sure the scrim is up before the app moves
                // delay starting scroll capture to make sure the scrim is up before the app moves
                mScreenshotView.post(() -> runBatchScrollCapture(response, owner));
                mViewProxy.post(() -> runBatchScrollCapture(response, owner));
            });
            });
        } catch (InterruptedException | ExecutionException e) {
        } catch (InterruptedException | ExecutionException e) {
            Log.e(TAG, "requestScrollCapture failed", e);
            Log.e(TAG, "requestScrollCapture failed", e);
@@ -794,19 +796,19 @@ public class ScreenshotController {
                return;
                return;
            } catch (InterruptedException | ExecutionException e) {
            } catch (InterruptedException | ExecutionException e) {
                Log.e(TAG, "Exception", e);
                Log.e(TAG, "Exception", e);
                mScreenshotView.restoreNonScrollingUi();
                mViewProxy.restoreNonScrollingUi();
                return;
                return;
            }
            }


            if (longScreenshot.getHeight() == 0) {
            if (longScreenshot.getHeight() == 0) {
                mScreenshotView.restoreNonScrollingUi();
                mViewProxy.restoreNonScrollingUi();
                return;
                return;
            }
            }


            mLongScreenshotHolder.setLongScreenshot(longScreenshot);
            mLongScreenshotHolder.setLongScreenshot(longScreenshot);
            mLongScreenshotHolder.setTransitionDestinationCallback(
            mLongScreenshotHolder.setTransitionDestinationCallback(
                    (transitionDestination, onTransitionEnd) -> {
                    (transitionDestination, onTransitionEnd) -> {
                        mScreenshotView.startLongScreenshotTransition(
                        mViewProxy.startLongScreenshotTransition(
                                transitionDestination, onTransitionEnd,
                                transitionDestination, onTransitionEnd,
                                longScreenshot);
                                longScreenshot);
                        // TODO: Do this via ActionIntentExecutor instead.
                        // TODO: Do this via ActionIntentExecutor instead.
@@ -882,10 +884,8 @@ public class ScreenshotController {
            }
            }
            mWindowManager.removeViewImmediate(decorView);
            mWindowManager.removeViewImmediate(decorView);
        }
        }
        // Ensure that we remove the input monitor

        if (mScreenshotView != null) {
        mViewProxy.stopInputListening();
            mScreenshotView.stopInputListening();
        }
    }
    }


    private void playCameraSoundIfNeeded() {
    private void playCameraSoundIfNeeded() {
@@ -932,7 +932,7 @@ public class ScreenshotController {
        }
        }


        mScreenshotAnimation =
        mScreenshotAnimation =
                mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
                mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash);
        if (onAnimationComplete != null) {
        if (onAnimationComplete != null) {
            mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
            mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
                @Override
                @Override
@@ -975,7 +975,7 @@ public class ScreenshotController {
                };
                };
        Pair<ActivityOptions, ExitTransitionCoordinator> transition =
        Pair<ActivityOptions, ExitTransitionCoordinator> transition =
                ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
                ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
                        Pair.create(mScreenshotView.getScreenshotPreview(),
                        Pair.create(mViewProxy.getScreenshotPreview(),
                                ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
                                ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));


        return transition;
        return transition;
@@ -999,7 +999,7 @@ public class ScreenshotController {
            mCurrentRequestCallback.onFinish();
            mCurrentRequestCallback.onFinish();
            mCurrentRequestCallback = null;
            mCurrentRequestCallback = null;
        }
        }
        mScreenshotView.reset();
        mViewProxy.reset();
        removeWindow();
        removeWindow();
        mScreenshotHandler.cancelTimeout();
        mScreenshotHandler.cancelTimeout();
    }
    }
@@ -1067,7 +1067,7 @@ public class ScreenshotController {
    }
    }


    private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
    private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
        mScreenshotView.setChipIntents(imageData);
        mViewProxy.setChipIntents(imageData);
    }
    }


    /**
    /**
@@ -1084,11 +1084,11 @@ public class ScreenshotController {
                        @Override
                        @Override
                        public void onAnimationEnd(Animator animation) {
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            super.onAnimationEnd(animation);
                            mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
                            mViewProxy.addQuickShareChip(quickShareData.quickShareAction);
                        }
                        }
                    });
                    });
                } else {
                } else {
                    mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
                    mViewProxy.addQuickShareChip(quickShareData.quickShareAction);
                }
                }
            });
            });
        }
        }
+8 −8
Original line number Original line Diff line number Diff line
@@ -102,7 +102,7 @@ import java.util.ArrayList;
public class ScreenshotView extends FrameLayout implements
public class ScreenshotView extends FrameLayout implements
        ViewTreeObserver.OnComputeInternalInsetsListener {
        ViewTreeObserver.OnComputeInternalInsetsListener {


    interface ScreenshotViewCallback {
    public interface ScreenshotViewCallback {
        void onUserInteraction();
        void onUserInteraction();


        void onAction(Intent intent, UserHandle owner, boolean overrideTransition);
        void onAction(Intent intent, UserHandle owner, boolean overrideTransition);
@@ -426,15 +426,15 @@ public class ScreenshotView extends FrameLayout implements
        return mScreenshotPreview;
        return mScreenshotPreview;
    }
    }


    /**
    void setUiEventLogger(UiEventLogger uiEventLogger) {
     * Set up the logger and callback on dismissal.
     *
     * Note: must be called before any other (non-constructor) method or null pointer exceptions
     * may occur.
     */
    void init(UiEventLogger uiEventLogger, ScreenshotViewCallback callbacks, FeatureFlags flags) {
        mUiEventLogger = uiEventLogger;
        mUiEventLogger = uiEventLogger;
    }

    void setCallbacks(ScreenshotViewCallback callbacks) {
        mCallbacks = callbacks;
        mCallbacks = callbacks;
    }

    void setFlags(FeatureFlags flags) {
        mFlags = flags;
        mFlags = flags;
    }
    }


+89 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.animation.Animator
import android.app.Notification
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.ScrollCaptureResponse
import android.view.View
import android.view.View.OnKeyListener
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.WindowInsets
import android.window.OnBackInvokedDispatcher
import com.android.internal.logging.UiEventLogger
import com.android.systemui.flags.FeatureFlags

/** Abstraction of the surface between ScreenshotController and ScreenshotView */
interface ScreenshotViewProxy {
    val view: ViewGroup
    val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener
    val screenshotPreview: View

    var defaultDisplay: Int
    var defaultTimeoutMillis: Long
    var onKeyListener: OnKeyListener?
    var flags: FeatureFlags?
    var packageName: String
    var logger: UiEventLogger?
    var callbacks: ScreenshotView.ScreenshotViewCallback?
    var screenshot: ScreenshotData?

    val isAttachedToWindow: Boolean
    val isDismissing: Boolean
    val isPendingSharedTransition: Boolean

    fun reset()
    fun updateInsets(insets: WindowInsets)
    fun updateOrientation(insets: WindowInsets)
    fun badgeScreenshot(userBadgedIcon: Drawable)
    fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator
    fun addQuickShareChip(quickShareAction: Notification.Action)
    fun setChipIntents(imageData: ScreenshotController.SavedImageData)
    fun animateDismissal()

    fun showScrollChip(packageName: String, onClick: Runnable)
    fun hideScrollChip()
    fun prepareScrollingTransition(
        response: ScrollCaptureResponse,
        screenBitmap: Bitmap,
        newScreenshot: Bitmap,
        screenshotTakenInPortrait: Boolean
    )
    fun startLongScreenshotTransition(
        transitionDestination: Rect,
        onTransitionEnd: Runnable,
        longScreenshot: ScrollCaptureController.LongScreenshot
    )
    fun restoreNonScrollingUi()

    fun stopInputListening()
    fun requestFocus()
    fun announceForAccessibility(string: String)
    fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener)
    fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher?
    fun getViewTreeObserver(): ViewTreeObserver
    fun post(runnable: Runnable)

    interface Factory {
        fun getProxy(context: Context): ScreenshotViewProxy
    }
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -75,7 +75,7 @@ public class ScrollCaptureController {
    private String mWindowOwner;
    private String mWindowOwner;
    private volatile boolean mCancelled;
    private volatile boolean mCancelled;


    static class LongScreenshot {
    public static class LongScreenshot {
        private final ImageTileSet mImageTileSet;
        private final ImageTileSet mImageTileSet;
        private final Session mSession;
        private final Session mSession;
        // TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy
        // TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy
Loading