Loading packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt 0 → 100644 +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) } } } packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +52 −52 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -272,7 +272,6 @@ public class ScreenshotController { respondToKeyDismissal(); }; private ScreenshotView mScreenshotView; private final MessageContainerController mMessageContainerController; private Bitmap mScreenBitmap; private SaveImageInBackgroundTask mSaveInBgTask; Loading Loading @@ -305,6 +304,7 @@ public class ScreenshotController { ScreenshotController( Context context, FeatureFlags flags, ScreenshotViewProxy.Factory viewProxyFactory, ScreenshotSmartActions screenshotSmartActions, ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory, ScrollCaptureClient scrollCaptureClient, Loading Loading @@ -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 Loading Loading @@ -461,7 +463,7 @@ public class ScreenshotController { // The window is focusable by default setWindowFocusable(true); mScreenshotView.requestFocus(); mViewProxy.requestFocus(); enqueueScrollCaptureRequest(screenshot.getUserHandle()); Loading @@ -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( Loading @@ -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()); } Loading @@ -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"); } Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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) { Loading @@ -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); Loading @@ -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; } Loading @@ -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 Loading Loading @@ -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); Loading @@ -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. Loading Loading @@ -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() { Loading Loading @@ -932,7 +932,7 @@ public class ScreenshotController { } mScreenshotAnimation = mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash); if (onAnimationComplete != null) { mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { @Override Loading Loading @@ -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; Loading @@ -999,7 +999,7 @@ public class ScreenshotController { mCurrentRequestCallback.onFinish(); mCurrentRequestCallback = null; } mScreenshotView.reset(); mViewProxy.reset(); removeWindow(); mScreenshotHandler.cancelTimeout(); } Loading Loading @@ -1067,7 +1067,7 @@ public class ScreenshotController { } private void doPostAnimation(ScreenshotController.SavedImageData imageData) { mScreenshotView.setChipIntents(imageData); mViewProxy.setChipIntents(imageData); } /** Loading @@ -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); } }); } Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +8 −8 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; } Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt 0 → 100644 +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 } } packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt 0 → 100644 +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) } } }
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +52 −52 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -272,7 +272,6 @@ public class ScreenshotController { respondToKeyDismissal(); }; private ScreenshotView mScreenshotView; private final MessageContainerController mMessageContainerController; private Bitmap mScreenBitmap; private SaveImageInBackgroundTask mSaveInBgTask; Loading Loading @@ -305,6 +304,7 @@ public class ScreenshotController { ScreenshotController( Context context, FeatureFlags flags, ScreenshotViewProxy.Factory viewProxyFactory, ScreenshotSmartActions screenshotSmartActions, ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory, ScrollCaptureClient scrollCaptureClient, Loading Loading @@ -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 Loading Loading @@ -461,7 +463,7 @@ public class ScreenshotController { // The window is focusable by default setWindowFocusable(true); mScreenshotView.requestFocus(); mViewProxy.requestFocus(); enqueueScrollCaptureRequest(screenshot.getUserHandle()); Loading @@ -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( Loading @@ -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()); } Loading @@ -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"); } Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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) { Loading @@ -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); Loading @@ -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; } Loading @@ -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 Loading Loading @@ -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); Loading @@ -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. Loading Loading @@ -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() { Loading Loading @@ -932,7 +932,7 @@ public class ScreenshotController { } mScreenshotAnimation = mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash); if (onAnimationComplete != null) { mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { @Override Loading Loading @@ -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; Loading @@ -999,7 +999,7 @@ public class ScreenshotController { mCurrentRequestCallback.onFinish(); mCurrentRequestCallback = null; } mScreenshotView.reset(); mViewProxy.reset(); removeWindow(); mScreenshotHandler.cancelTimeout(); } Loading Loading @@ -1067,7 +1067,7 @@ public class ScreenshotController { } private void doPostAnimation(ScreenshotController.SavedImageData imageData) { mScreenshotView.setChipIntents(imageData); mViewProxy.setChipIntents(imageData); } /** Loading @@ -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); } }); } Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +8 −8 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; } Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt 0 → 100644 +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 } }
packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +1 −1 Original line number Diff line number Diff line Loading @@ -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