Loading packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt +3 −3 Original line number Diff line number Diff line Loading @@ -34,9 +34,9 @@ import androidx.appcompat.content.res.AppCompatResources import com.android.internal.logging.UiEventLogger import com.android.systemui.flags.FeatureFlags import com.android.systemui.res.R import com.android.systemui.screenshot.scroll.ScrollCaptureController import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER import com.android.systemui.screenshot.scroll.ScrollCaptureController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject Loading Loading @@ -113,7 +113,7 @@ constructor( override fun setChipIntents(imageData: ScreenshotController.SavedImageData) = view.setChipIntents(imageData) override fun requestDismissal(event: ScreenshotEvent) { override fun requestDismissal(event: ScreenshotEvent?) { if (DEBUG_DISMISS) { Log.d(TAG, "screenshot dismissal requested") } Loading @@ -124,7 +124,7 @@ constructor( } return } logger.log(event, 0, packageName) event?.let { logger.log(event, 0, packageName) } view.animateDismissal() } Loading packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +1 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image, mParams.owner); mImageData.subject = getSubjectString(mImageTime); mImageData.imageTime = mImageTime; mParams.mActionsReadyListener.onActionsReady(mImageData); if (DEBUG_CALLBACK) { Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt +107 −1 Original line number Diff line number Diff line Loading @@ -17,24 +17,37 @@ package com.android.systemui.screenshot import android.app.ActivityOptions import android.app.BroadcastOptions import android.app.ExitTransitionCoordinator import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Process import android.os.UserHandle import android.provider.DeviceConfig import android.util.Log import android.util.Pair import androidx.appcompat.content.res.AppCompatResources import com.android.app.tracing.coroutines.launch import com.android.internal.config.sysui.SystemUiDeviceConfigFlags import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.res.R import com.android.systemui.screenshot.ActionIntentCreator.createEdit import com.android.systemui.screenshot.ActionIntentCreator.createShareWithSubject import com.android.systemui.screenshot.ScreenshotController.SavedImageData import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.text.DateFormat import java.util.Date import kotlinx.coroutines.CoroutineScope /** Loading @@ -48,7 +61,9 @@ interface ScreenshotActionsProvider { interface Factory { fun create( request: ScreenshotData, requestId: String, windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>, requestDismissal: () -> Unit, ): ScreenshotActionsProvider } } Loading @@ -59,9 +74,13 @@ constructor( private val context: Context, private val viewModel: ScreenshotViewModel, private val actionExecutor: ActionIntentExecutor, private val smartActionsProvider: SmartActionsProvider, private val uiEventLogger: UiEventLogger, @Application private val applicationScope: CoroutineScope, @Assisted val request: ScreenshotData, @Assisted val requestId: String, @Assisted val windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>, @Assisted val requestDismissal: () -> Unit, ) : ScreenshotActionsProvider { private var pendingAction: ((SavedImageData) -> Unit)? = null private var result: SavedImageData? = null Loading @@ -70,6 +89,7 @@ constructor( init { viewModel.setPreviewAction { debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" } uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString) onDeferrableActionTapped { result -> startSharedTransition(createEdit(result.uri, context), true) } Loading @@ -81,6 +101,7 @@ constructor( context.resources.getString(R.string.screenshot_edit_description), ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" } uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString) onDeferrableActionTapped { result -> startSharedTransition(createEdit(result.uri, context), true) } Loading @@ -93,11 +114,46 @@ constructor( context.resources.getString(R.string.screenshot_share_description), ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Share tapped" } uiEventLogger.log(SCREENSHOT_SHARE_TAPPED, 0, request.packageNameString) onDeferrableActionTapped { result -> startSharedTransition(createShareWithSubject(result.uri, result.subject), false) } } ) if (smartActionsEnabled(request.userHandle ?: Process.myUserHandle())) { smartActionsProvider.requestQuickShare(request, requestId) { quickShare -> if (!quickShare.actionIntent.isImmutable) { viewModel.addAction( ActionButtonViewModel( quickShare.getIcon().loadDrawable(context), quickShare.title, quickShare.title ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Quickshare tapped" } onDeferrableActionTapped { result -> uiEventLogger.log( SCREENSHOT_SMART_ACTION_TAPPED, 0, request.packageNameString ) sendPendingIntent( smartActionsProvider .wrapIntent( quickShare, result.uri, result.subject, requestId ) .actionIntent ) } } ) } else { Log.w(TAG, "Received immutable quick share pending intent; ignoring") } } } } override fun setCompletedScreenshot(result: SavedImageData) { Loading @@ -105,12 +161,30 @@ constructor( Log.e(TAG, "Got a second completed screenshot for existing request!") return } if (result.uri == null || result.owner == null || result.subject == null) { if (result.uri == null || result.owner == null || result.imageTime == null) { Log.e(TAG, "Invalid result provided!") return } if (result.subject == null) { result.subject = getSubjectString(result.imageTime) } this.result = result pendingAction?.invoke(result) if (smartActionsEnabled(result.owner)) { smartActionsProvider.requestSmartActions(request, requestId, result) { smartActions -> viewModel.addActions( smartActions.map { ActionButtonViewModel( it.getIcon().loadDrawable(context), it.title, it.title ) { sendPendingIntent(it.actionIntent) } } ) } } } override fun isPendingSharedTransition(): Boolean { Loading @@ -134,15 +208,47 @@ constructor( } } private fun sendPendingIntent(pendingIntent: PendingIntent) { try { val options = BroadcastOptions.makeBasic() options.setInteractive(true) options.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED ) pendingIntent.send(options.toBundle()) requestDismissal.invoke() } catch (e: PendingIntent.CanceledException) { Log.e(TAG, "Intent cancelled", e) } } private fun smartActionsEnabled(user: UserHandle): Boolean { val savingToOtherUser = user != Process.myUserHandle() return !savingToOtherUser && DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true ) } private fun getSubjectString(imageTime: Long): String { val subjectDate = DateFormat.getDateTimeInstance().format(Date(imageTime)) return String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate) } @AssistedFactory interface Factory : ScreenshotActionsProvider.Factory { override fun create( request: ScreenshotData, requestId: String, windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>, requestDismissal: () -> Unit, ): DefaultScreenshotActionsProvider } companion object { private const val TAG = "ScreenshotActionsProvider" private const val SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)" } } packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +66 −20 Original line number Diff line number Diff line Loading @@ -96,7 +96,10 @@ import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; import kotlin.Unit; import java.util.List; import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; Loading Loading @@ -167,6 +170,7 @@ public class ScreenshotController { public Notification.Action quickShareAction; public UserHandle owner; public String subject; // Title for sharing public Long imageTime; // Time at which screenshot was saved /** * Used to reset the return data on error Loading @@ -176,6 +180,7 @@ public class ScreenshotController { smartActions = null; quickShareAction = null; subject = null; imageTime = null; } } Loading Loading @@ -261,11 +266,9 @@ public class ScreenshotController { private SaveImageInBackgroundTask mSaveInBgTask; private boolean mScreenshotTakenInPortrait; private boolean mBlockAttach; private ScreenshotActionsProvider mActionsProvider; private Animator mScreenshotAnimation; private RequestCallback mCurrentRequestCallback; private ScreenshotActionsProvider mActionsProvider; private String mPackageName = ""; private final BroadcastReceiver mCopyBroadcastReceiver; Loading Loading @@ -317,6 +320,7 @@ public class ScreenshotController { @Assisted boolean showUIOnExternalDisplay ) { mScreenshotSmartActions = screenshotSmartActions; mActionsProviderFactory = actionsProviderFactory; mNotificationsController = screenshotNotificationsControllerFactory.create(displayId); mScrollCaptureClient = scrollCaptureClient; mUiEventLogger = uiEventLogger; Loading Loading @@ -347,7 +351,6 @@ public class ScreenshotController { mAssistContentRequester = assistContentRequester; mViewProxy = viewProxyFactory.getProxy(mContext, mDisplayId); mActionsProviderFactory = actionsProviderFactory; mScreenshotHandler.setOnTimeoutRunnable(() -> { if (DEBUG_UI) { Loading Loading @@ -441,8 +444,19 @@ public class ScreenshotController { return; } if (screenshotShelfUi()) { final UUID requestId = UUID.randomUUID(); final String screenshotId = String.format("Screenshot_%s", requestId); mActionsProvider = mActionsProviderFactory.create(screenshot, screenshotId, this::createWindowTransition, () -> { mViewProxy.requestDismissal(null); return Unit.INSTANCE; }); saveScreenshotInBackground(screenshot, requestId, finisher); } else { saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher, this::showUiOnActionsReady, this::showUiOnQuickShareActionReady); } // The window is focusable by default setWindowFocusable(true); Loading Loading @@ -477,8 +491,10 @@ public class ScreenshotController { // ignore system bar insets for the purpose of window layout mWindow.getDecorView().setOnApplyWindowInsetsListener( (v, insets) -> WindowInsets.CONSUMED); if (!screenshotShelfUi()) { mScreenshotHandler.cancelTimeout(); // restarted after animation } } private boolean shouldShowUi() { return mDisplayId == Display.DEFAULT_DISPLAY || mShowUIOnExternalDisplay; Loading @@ -497,11 +513,6 @@ public class ScreenshotController { mViewProxy.reset(); if (screenshotShelfUi()) { mActionsProvider = mActionsProviderFactory.create(screenshot, this::createWindowTransition); } if (mViewProxy.isAttachedToWindow()) { // if we didn't already dismiss for another reason if (!mViewProxy.isDismissing()) { Loading Loading @@ -921,6 +932,39 @@ public class ScreenshotController { mScreenshotHandler.cancelTimeout(); } private void saveScreenshotInBackground( ScreenshotData screenshot, UUID requestId, Consumer<Uri> finisher) { ListenableFuture<ImageExporter.Result> future = mImageExporter.export(mBgExecutor, requestId, screenshot.getBitmap(), screenshot.getUserHandle(), mDisplayId); future.addListener(() -> { try { ImageExporter.Result result = future.get(); Log.d(TAG, "Saved screenshot: " + result); logScreenshotResultStatus(result.uri, screenshot.getUserHandle()); mScreenshotHandler.resetTimeout(); if (result.uri != null) { final SavedImageData savedImageData = new SavedImageData(); savedImageData.uri = result.uri; savedImageData.owner = screenshot.getUserHandle(); savedImageData.imageTime = result.timestamp; mActionsProvider.setCompletedScreenshot(savedImageData); mViewProxy.setChipIntents(savedImageData); } if (DEBUG_CALLBACK) { Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) " + "finisher.accept(\"" + result.uri + "\""); } finisher.accept(result.uri); } catch (Exception e) { Log.d(TAG, "Failed to store screenshot", e); if (DEBUG_CALLBACK) { Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)"); } finisher.accept(null); } }, mMainExecutor); } /** * Creates a new worker thread and saves the screenshot to the media store. */ Loading Loading @@ -958,11 +1002,6 @@ public class ScreenshotController { logSuccessOnActionsReady(imageData); mScreenshotHandler.resetTimeout(); if (screenshotShelfUi()) { mActionsProvider.setCompletedScreenshot(imageData); return; } if (imageData.uri != null) { if (DEBUG_UI) { Log.d(TAG, "Showing UI actions"); Loading Loading @@ -1014,20 +1053,27 @@ public class ScreenshotController { /** * Logs success/failure of the screenshot saving task, and shows an error if it failed. */ private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) { if (imageData.uri == null) { private void logScreenshotResultStatus(Uri uri, UserHandle owner) { if (uri == null) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, mPackageName); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_save_text); } else { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName); if (mUserManager.isManagedProfile(imageData.owner.getIdentifier())) { if (mUserManager.isManagedProfile(owner.getIdentifier())) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0, mPackageName); } } } /** * Logs success/failure of the screenshot saving task, and shows an error if it failed. */ private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) { logScreenshotResultStatus(imageData.uri, imageData.owner); } private boolean isUserSetupComplete(UserHandle owner) { return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0) .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1; Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt +2 −2 Original line number Diff line number Diff line Loading @@ -98,7 +98,7 @@ constructor( override fun setChipIntents(imageData: SavedImageData) {} override fun requestDismissal(event: ScreenshotEvent) { override fun requestDismissal(event: ScreenshotEvent?) { debugLog(DEBUG_DISMISS) { "screenshot dismissal requested: $event" } // If we're already animating out, don't restart the animation Loading @@ -106,7 +106,7 @@ constructor( debugLog(DEBUG_DISMISS) { "Already dismissing, ignoring duplicate command $event" } return } logger.log(event, 0, packageName) event?.let { logger.log(it, 0, packageName) } val animator = animationController.getExitAnimation() animator.addListener( object : AnimatorListenerAdapter() { Loading Loading
packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt +3 −3 Original line number Diff line number Diff line Loading @@ -34,9 +34,9 @@ import androidx.appcompat.content.res.AppCompatResources import com.android.internal.logging.UiEventLogger import com.android.systemui.flags.FeatureFlags import com.android.systemui.res.R import com.android.systemui.screenshot.scroll.ScrollCaptureController import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER import com.android.systemui.screenshot.scroll.ScrollCaptureController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject Loading Loading @@ -113,7 +113,7 @@ constructor( override fun setChipIntents(imageData: ScreenshotController.SavedImageData) = view.setChipIntents(imageData) override fun requestDismissal(event: ScreenshotEvent) { override fun requestDismissal(event: ScreenshotEvent?) { if (DEBUG_DISMISS) { Log.d(TAG, "screenshot dismissal requested") } Loading @@ -124,7 +124,7 @@ constructor( } return } logger.log(event, 0, packageName) event?.let { logger.log(event, 0, packageName) } view.animateDismissal() } Loading
packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +1 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image, mParams.owner); mImageData.subject = getSubjectString(mImageTime); mImageData.imageTime = mImageTime; mParams.mActionsReadyListener.onActionsReady(mImageData); if (DEBUG_CALLBACK) { Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt +107 −1 Original line number Diff line number Diff line Loading @@ -17,24 +17,37 @@ package com.android.systemui.screenshot import android.app.ActivityOptions import android.app.BroadcastOptions import android.app.ExitTransitionCoordinator import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Process import android.os.UserHandle import android.provider.DeviceConfig import android.util.Log import android.util.Pair import androidx.appcompat.content.res.AppCompatResources import com.android.app.tracing.coroutines.launch import com.android.internal.config.sysui.SystemUiDeviceConfigFlags import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.res.R import com.android.systemui.screenshot.ActionIntentCreator.createEdit import com.android.systemui.screenshot.ActionIntentCreator.createShareWithSubject import com.android.systemui.screenshot.ScreenshotController.SavedImageData import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.text.DateFormat import java.util.Date import kotlinx.coroutines.CoroutineScope /** Loading @@ -48,7 +61,9 @@ interface ScreenshotActionsProvider { interface Factory { fun create( request: ScreenshotData, requestId: String, windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>, requestDismissal: () -> Unit, ): ScreenshotActionsProvider } } Loading @@ -59,9 +74,13 @@ constructor( private val context: Context, private val viewModel: ScreenshotViewModel, private val actionExecutor: ActionIntentExecutor, private val smartActionsProvider: SmartActionsProvider, private val uiEventLogger: UiEventLogger, @Application private val applicationScope: CoroutineScope, @Assisted val request: ScreenshotData, @Assisted val requestId: String, @Assisted val windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>, @Assisted val requestDismissal: () -> Unit, ) : ScreenshotActionsProvider { private var pendingAction: ((SavedImageData) -> Unit)? = null private var result: SavedImageData? = null Loading @@ -70,6 +89,7 @@ constructor( init { viewModel.setPreviewAction { debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" } uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString) onDeferrableActionTapped { result -> startSharedTransition(createEdit(result.uri, context), true) } Loading @@ -81,6 +101,7 @@ constructor( context.resources.getString(R.string.screenshot_edit_description), ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" } uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString) onDeferrableActionTapped { result -> startSharedTransition(createEdit(result.uri, context), true) } Loading @@ -93,11 +114,46 @@ constructor( context.resources.getString(R.string.screenshot_share_description), ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Share tapped" } uiEventLogger.log(SCREENSHOT_SHARE_TAPPED, 0, request.packageNameString) onDeferrableActionTapped { result -> startSharedTransition(createShareWithSubject(result.uri, result.subject), false) } } ) if (smartActionsEnabled(request.userHandle ?: Process.myUserHandle())) { smartActionsProvider.requestQuickShare(request, requestId) { quickShare -> if (!quickShare.actionIntent.isImmutable) { viewModel.addAction( ActionButtonViewModel( quickShare.getIcon().loadDrawable(context), quickShare.title, quickShare.title ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Quickshare tapped" } onDeferrableActionTapped { result -> uiEventLogger.log( SCREENSHOT_SMART_ACTION_TAPPED, 0, request.packageNameString ) sendPendingIntent( smartActionsProvider .wrapIntent( quickShare, result.uri, result.subject, requestId ) .actionIntent ) } } ) } else { Log.w(TAG, "Received immutable quick share pending intent; ignoring") } } } } override fun setCompletedScreenshot(result: SavedImageData) { Loading @@ -105,12 +161,30 @@ constructor( Log.e(TAG, "Got a second completed screenshot for existing request!") return } if (result.uri == null || result.owner == null || result.subject == null) { if (result.uri == null || result.owner == null || result.imageTime == null) { Log.e(TAG, "Invalid result provided!") return } if (result.subject == null) { result.subject = getSubjectString(result.imageTime) } this.result = result pendingAction?.invoke(result) if (smartActionsEnabled(result.owner)) { smartActionsProvider.requestSmartActions(request, requestId, result) { smartActions -> viewModel.addActions( smartActions.map { ActionButtonViewModel( it.getIcon().loadDrawable(context), it.title, it.title ) { sendPendingIntent(it.actionIntent) } } ) } } } override fun isPendingSharedTransition(): Boolean { Loading @@ -134,15 +208,47 @@ constructor( } } private fun sendPendingIntent(pendingIntent: PendingIntent) { try { val options = BroadcastOptions.makeBasic() options.setInteractive(true) options.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED ) pendingIntent.send(options.toBundle()) requestDismissal.invoke() } catch (e: PendingIntent.CanceledException) { Log.e(TAG, "Intent cancelled", e) } } private fun smartActionsEnabled(user: UserHandle): Boolean { val savingToOtherUser = user != Process.myUserHandle() return !savingToOtherUser && DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true ) } private fun getSubjectString(imageTime: Long): String { val subjectDate = DateFormat.getDateTimeInstance().format(Date(imageTime)) return String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate) } @AssistedFactory interface Factory : ScreenshotActionsProvider.Factory { override fun create( request: ScreenshotData, requestId: String, windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>, requestDismissal: () -> Unit, ): DefaultScreenshotActionsProvider } companion object { private const val TAG = "ScreenshotActionsProvider" private const val SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)" } }
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +66 −20 Original line number Diff line number Diff line Loading @@ -96,7 +96,10 @@ import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; import kotlin.Unit; import java.util.List; import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; Loading Loading @@ -167,6 +170,7 @@ public class ScreenshotController { public Notification.Action quickShareAction; public UserHandle owner; public String subject; // Title for sharing public Long imageTime; // Time at which screenshot was saved /** * Used to reset the return data on error Loading @@ -176,6 +180,7 @@ public class ScreenshotController { smartActions = null; quickShareAction = null; subject = null; imageTime = null; } } Loading Loading @@ -261,11 +266,9 @@ public class ScreenshotController { private SaveImageInBackgroundTask mSaveInBgTask; private boolean mScreenshotTakenInPortrait; private boolean mBlockAttach; private ScreenshotActionsProvider mActionsProvider; private Animator mScreenshotAnimation; private RequestCallback mCurrentRequestCallback; private ScreenshotActionsProvider mActionsProvider; private String mPackageName = ""; private final BroadcastReceiver mCopyBroadcastReceiver; Loading Loading @@ -317,6 +320,7 @@ public class ScreenshotController { @Assisted boolean showUIOnExternalDisplay ) { mScreenshotSmartActions = screenshotSmartActions; mActionsProviderFactory = actionsProviderFactory; mNotificationsController = screenshotNotificationsControllerFactory.create(displayId); mScrollCaptureClient = scrollCaptureClient; mUiEventLogger = uiEventLogger; Loading Loading @@ -347,7 +351,6 @@ public class ScreenshotController { mAssistContentRequester = assistContentRequester; mViewProxy = viewProxyFactory.getProxy(mContext, mDisplayId); mActionsProviderFactory = actionsProviderFactory; mScreenshotHandler.setOnTimeoutRunnable(() -> { if (DEBUG_UI) { Loading Loading @@ -441,8 +444,19 @@ public class ScreenshotController { return; } if (screenshotShelfUi()) { final UUID requestId = UUID.randomUUID(); final String screenshotId = String.format("Screenshot_%s", requestId); mActionsProvider = mActionsProviderFactory.create(screenshot, screenshotId, this::createWindowTransition, () -> { mViewProxy.requestDismissal(null); return Unit.INSTANCE; }); saveScreenshotInBackground(screenshot, requestId, finisher); } else { saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher, this::showUiOnActionsReady, this::showUiOnQuickShareActionReady); } // The window is focusable by default setWindowFocusable(true); Loading Loading @@ -477,8 +491,10 @@ public class ScreenshotController { // ignore system bar insets for the purpose of window layout mWindow.getDecorView().setOnApplyWindowInsetsListener( (v, insets) -> WindowInsets.CONSUMED); if (!screenshotShelfUi()) { mScreenshotHandler.cancelTimeout(); // restarted after animation } } private boolean shouldShowUi() { return mDisplayId == Display.DEFAULT_DISPLAY || mShowUIOnExternalDisplay; Loading @@ -497,11 +513,6 @@ public class ScreenshotController { mViewProxy.reset(); if (screenshotShelfUi()) { mActionsProvider = mActionsProviderFactory.create(screenshot, this::createWindowTransition); } if (mViewProxy.isAttachedToWindow()) { // if we didn't already dismiss for another reason if (!mViewProxy.isDismissing()) { Loading Loading @@ -921,6 +932,39 @@ public class ScreenshotController { mScreenshotHandler.cancelTimeout(); } private void saveScreenshotInBackground( ScreenshotData screenshot, UUID requestId, Consumer<Uri> finisher) { ListenableFuture<ImageExporter.Result> future = mImageExporter.export(mBgExecutor, requestId, screenshot.getBitmap(), screenshot.getUserHandle(), mDisplayId); future.addListener(() -> { try { ImageExporter.Result result = future.get(); Log.d(TAG, "Saved screenshot: " + result); logScreenshotResultStatus(result.uri, screenshot.getUserHandle()); mScreenshotHandler.resetTimeout(); if (result.uri != null) { final SavedImageData savedImageData = new SavedImageData(); savedImageData.uri = result.uri; savedImageData.owner = screenshot.getUserHandle(); savedImageData.imageTime = result.timestamp; mActionsProvider.setCompletedScreenshot(savedImageData); mViewProxy.setChipIntents(savedImageData); } if (DEBUG_CALLBACK) { Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) " + "finisher.accept(\"" + result.uri + "\""); } finisher.accept(result.uri); } catch (Exception e) { Log.d(TAG, "Failed to store screenshot", e); if (DEBUG_CALLBACK) { Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)"); } finisher.accept(null); } }, mMainExecutor); } /** * Creates a new worker thread and saves the screenshot to the media store. */ Loading Loading @@ -958,11 +1002,6 @@ public class ScreenshotController { logSuccessOnActionsReady(imageData); mScreenshotHandler.resetTimeout(); if (screenshotShelfUi()) { mActionsProvider.setCompletedScreenshot(imageData); return; } if (imageData.uri != null) { if (DEBUG_UI) { Log.d(TAG, "Showing UI actions"); Loading Loading @@ -1014,20 +1053,27 @@ public class ScreenshotController { /** * Logs success/failure of the screenshot saving task, and shows an error if it failed. */ private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) { if (imageData.uri == null) { private void logScreenshotResultStatus(Uri uri, UserHandle owner) { if (uri == null) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, mPackageName); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_save_text); } else { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName); if (mUserManager.isManagedProfile(imageData.owner.getIdentifier())) { if (mUserManager.isManagedProfile(owner.getIdentifier())) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0, mPackageName); } } } /** * Logs success/failure of the screenshot saving task, and shows an error if it failed. */ private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) { logScreenshotResultStatus(imageData.uri, imageData.owner); } private boolean isUserSetupComplete(UserHandle owner) { return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0) .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1; Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt +2 −2 Original line number Diff line number Diff line Loading @@ -98,7 +98,7 @@ constructor( override fun setChipIntents(imageData: SavedImageData) {} override fun requestDismissal(event: ScreenshotEvent) { override fun requestDismissal(event: ScreenshotEvent?) { debugLog(DEBUG_DISMISS) { "screenshot dismissal requested: $event" } // If we're already animating out, don't restart the animation Loading @@ -106,7 +106,7 @@ constructor( debugLog(DEBUG_DISMISS) { "Already dismissing, ignoring duplicate command $event" } return } logger.log(event, 0, packageName) event?.let { logger.log(it, 0, packageName) } val animator = animationController.getExitAnimation() animator.addListener( object : AnimatorListenerAdapter() { Loading