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 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 Diff line number Diff line
@@ -65,7 +65,6 @@ import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
@@ -165,7 +164,7 @@ public class ScreenshotController {
    /**
     * Structure returned by the SaveImageInBackgroundTask
     */
    static class SavedImageData {
    public static class SavedImageData {
        public Uri uri;
        public List<Notification.Action> smartActions;
        public Notification.Action quickShareAction;
@@ -237,6 +236,7 @@ public class ScreenshotController {

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

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

        mViewProxy = viewProxyFactory.getProxy(mContext);

        mAccessibilityManager = AccessibilityManager.getInstance(mContext);

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

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

        enqueueScrollCaptureRequest(screenshot.getUserHandle());

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

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

        // ignore system bar insets for the purpose of window layout
        mWindow.getDecorView().setOnApplyWindowInsetsListener(
@@ -503,31 +505,31 @@ public class ScreenshotController {
    void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
        withWindowAttached(() -> {
            if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
                mScreenshotView.announceForAccessibility(mContext.getResources().getString(
                mViewProxy.announceForAccessibility(mContext.getResources().getString(
                        R.string.screenshot_saving_work_profile_title));
            } else {
                mScreenshotView.announceForAccessibility(
                mViewProxy.announceForAccessibility(
                        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 (!mScreenshotView.isDismissing()) {
            if (!mViewProxy.isDismissing()) {
                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
                        oldPackageName);
            }
            if (DEBUG_WINDOW) {
                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());
    }

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

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

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

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

@@ -611,11 +610,12 @@ public class ScreenshotController {
                        if (DEBUG_INPUT) {
                            Log.d(TAG, "Unregistering Predictive Back callback");
                        }
                        mScreenshotView.findOnBackInvokedDispatcher()
                        mViewProxy.findOnBackInvokedDispatcher()
                                .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
                    }
                });
        mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
        mViewProxy.setLogger(mUiEventLogger);
        mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() {
            @Override
            public void onUserInteraction() {
                if (DEBUG_INPUT) {
@@ -640,11 +640,12 @@ public class ScreenshotController {
                // TODO(159460485): Remove this when focus is handled properly in the system
                setWindowFocusable(false);
            }
        }, mFlags);
        mScreenshotView.setDefaultDisplay(mDisplayId);
        mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
        });
        mViewProxy.setFlags(mFlags);
        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 (DEBUG_INPUT) {
                    Log.d(TAG, "onKeyEvent: " + keyCode);
@@ -658,23 +659,24 @@ public class ScreenshotController {
        if (DEBUG_WINDOW) {
            Log.d(TAG, "adding OnComputeInternalInsetsListener");
        }
        mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
        mViewProxy.getViewTreeObserver().addOnComputeInternalInsetsListener(
                mViewProxy.getInternalInsetsListener());
        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,
            Runnable onAnimationComplete) {
        mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
        mViewProxy.getViewTreeObserver().addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        if (DEBUG_WINDOW) {
                            Log.d(TAG, "onPreDraw: startAnimation");
                        }
                        mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
                        mViewProxy.getViewTreeObserver().removeOnPreDrawListener(this);
                        startAnimation(screenRect, showFlash, onAnimationComplete);
                        return true;
                    }
@@ -694,13 +696,13 @@ public class ScreenshotController {
                            if (mConfigChanges.applyNewConfig(mContext.getResources())) {
                                // Hide the scroll chip until we know it's available in this
                                // orientation
                                mScreenshotView.hideScrollChip();
                                mViewProxy.hideScrollChip();
                                // Delay scroll capture eval a bit to allow the underlying activity
                                // to set up in the new orientation.
                                mScreenshotHandler.postDelayed(() -> {
                                    requestScrollCapture(owner);
                                }, 150);
                                mScreenshotView.updateInsets(
                                mViewProxy.updateInsets(
                                        mWindowManager.getCurrentWindowMetrics().getWindowInsets());
                                // Screenshot animation calculations won't be valid anymore,
                                // so just end
@@ -759,16 +761,16 @@ public class ScreenshotController {
                    + mLastScrollCaptureResponse.getWindowTitle() + "]");

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

                mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
                mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
                        mScreenshotTakenInPortrait);
                // 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) {
            Log.e(TAG, "requestScrollCapture failed", e);
@@ -794,19 +796,19 @@ public class ScreenshotController {
                return;
            } catch (InterruptedException | ExecutionException e) {
                Log.e(TAG, "Exception", e);
                mScreenshotView.restoreNonScrollingUi();
                mViewProxy.restoreNonScrollingUi();
                return;
            }

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

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

        mViewProxy.stopInputListening();
    }

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

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

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

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

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

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

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

    /**
     * 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) {
    void setUiEventLogger(UiEventLogger uiEventLogger) {
        mUiEventLogger = uiEventLogger;
    }

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

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

+89 −0
Original line number 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 Diff line number Diff line
@@ -75,7 +75,7 @@ public class ScrollCaptureController {
    private String mWindowOwner;
    private volatile boolean mCancelled;

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