Merge "Work profile screenshots: save to owner" into tm-qpr-dev

This commit is contained in:
Matt Casey
2022-10-04 20:56:45 +00:00
committed by Android (Google) Code Review
8 changed files with 83 additions and 56 deletions

View File

@@ -19,6 +19,7 @@ package com.android.systemui.screenshot;
import static android.os.FileUtils.closeQuietly; import static android.os.FileUtils.closeQuietly;
import android.annotation.IntRange; import android.annotation.IntRange;
import android.content.ContentProvider;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -29,6 +30,7 @@ import android.os.Environment;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.Trace; import android.os.Trace;
import android.os.UserHandle;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.util.Log; import android.util.Log;
@@ -142,8 +144,9 @@ class ImageExporter {
* *
* @return a listenable future result * @return a listenable future result
*/ */
ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap) { ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
return export(executor, requestId, bitmap, ZonedDateTime.now()); UserHandle owner) {
return export(executor, requestId, bitmap, ZonedDateTime.now(), owner);
} }
/** /**
@@ -155,10 +158,10 @@ class ImageExporter {
* @return a listenable future result * @return a listenable future result
*/ */
ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap, ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
ZonedDateTime captureTime) { ZonedDateTime captureTime, UserHandle owner) {
final Task task = new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat, final Task task = new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat,
mQuality, /* publish */ true); mQuality, /* publish */ true, owner);
return CallbackToFutureAdapter.getFuture( return CallbackToFutureAdapter.getFuture(
(completer) -> { (completer) -> {
@@ -174,28 +177,6 @@ class ImageExporter {
); );
} }
/**
* Delete the entry.
*
* @param executor the thread for execution
* @param uri the uri of the image to publish
*
* @return a listenable future result
*/
ListenableFuture<Result> delete(Executor executor, Uri uri) {
return CallbackToFutureAdapter.getFuture((completer) -> {
executor.execute(() -> {
mResolver.delete(uri, null);
Result result = new Result();
result.uri = uri;
result.deleted = true;
completer.set(result);
});
return "ContentResolver#delete";
});
}
static class Result { static class Result {
Uri uri; Uri uri;
UUID requestId; UUID requestId;
@@ -203,7 +184,6 @@ class ImageExporter {
long timestamp; long timestamp;
CompressFormat format; CompressFormat format;
boolean published; boolean published;
boolean deleted;
@Override @Override
public String toString() { public String toString() {
@@ -214,7 +194,6 @@ class ImageExporter {
sb.append(", timestamp=").append(timestamp); sb.append(", timestamp=").append(timestamp);
sb.append(", format=").append(format); sb.append(", format=").append(format);
sb.append(", published=").append(published); sb.append(", published=").append(published);
sb.append(", deleted=").append(deleted);
sb.append('}'); sb.append('}');
return sb.toString(); return sb.toString();
} }
@@ -227,17 +206,19 @@ class ImageExporter {
private final ZonedDateTime mCaptureTime; private final ZonedDateTime mCaptureTime;
private final CompressFormat mFormat; private final CompressFormat mFormat;
private final int mQuality; private final int mQuality;
private final UserHandle mOwner;
private final String mFileName; private final String mFileName;
private final boolean mPublish; private final boolean mPublish;
Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime, Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime,
CompressFormat format, int quality, boolean publish) { CompressFormat format, int quality, boolean publish, UserHandle owner) {
mResolver = resolver; mResolver = resolver;
mRequestId = requestId; mRequestId = requestId;
mBitmap = bitmap; mBitmap = bitmap;
mCaptureTime = captureTime; mCaptureTime = captureTime;
mFormat = format; mFormat = format;
mQuality = quality; mQuality = quality;
mOwner = owner;
mFileName = createFilename(mCaptureTime, mFormat); mFileName = createFilename(mCaptureTime, mFormat);
mPublish = publish; mPublish = publish;
} }
@@ -253,7 +234,7 @@ class ImageExporter {
start = Instant.now(); start = Instant.now();
} }
uri = createEntry(mResolver, mFormat, mCaptureTime, mFileName); uri = createEntry(mResolver, mFormat, mCaptureTime, mFileName, mOwner);
throwIfInterrupted(); throwIfInterrupted();
writeImage(mResolver, mBitmap, mFormat, mQuality, uri); writeImage(mResolver, mBitmap, mFormat, mQuality, uri);
@@ -297,15 +278,20 @@ class ImageExporter {
} }
private static Uri createEntry(ContentResolver resolver, CompressFormat format, private static Uri createEntry(ContentResolver resolver, CompressFormat format,
ZonedDateTime time, String fileName) throws ImageExportException { ZonedDateTime time, String fileName, UserHandle owner) throws ImageExportException {
Trace.beginSection("ImageExporter_createEntry"); Trace.beginSection("ImageExporter_createEntry");
try { try {
final ContentValues values = createMetadata(time, format, fileName); final ContentValues values = createMetadata(time, format, fileName);
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
if (UserHandle.myUserId() != owner.getIdentifier()) {
baseUri = ContentProvider.maybeAddUserId(baseUri, owner.getIdentifier());
}
Uri uri = resolver.insert(baseUri, values);
if (uri == null) { if (uri == null) {
throw new ImageExportException(RESOLVER_INSERT_RETURNED_NULL); throw new ImageExportException(RESOLVER_INSERT_RETURNED_NULL);
} }
Log.d(TAG, "Inserted new URI: " + uri);
return uri; return uri;
} finally { } finally {
Trace.endSection(); Trace.endSection();

View File

@@ -30,6 +30,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@@ -387,7 +388,9 @@ public class LongScreenshotActivity extends Activity {
mOutputBitmap = renderBitmap(drawable, bounds); mOutputBitmap = renderBitmap(drawable, bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now()); mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now(),
// TODO: Owner must match the owner of the captured window.
Process.myUserHandle());
exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor); exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
} }

View File

@@ -48,6 +48,8 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@@ -71,6 +73,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"; private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
private final Context mContext; private final Context mContext;
private FeatureFlags mFlags;
private final ScreenshotSmartActions mScreenshotSmartActions; private final ScreenshotSmartActions mScreenshotSmartActions;
private final ScreenshotController.SaveImageInBackgroundData mParams; private final ScreenshotController.SaveImageInBackgroundData mParams;
private final ScreenshotController.SavedImageData mImageData; private final ScreenshotController.SavedImageData mImageData;
@@ -84,7 +87,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final ImageExporter mImageExporter; private final ImageExporter mImageExporter;
private long mImageTime; private long mImageTime;
SaveImageInBackgroundTask(Context context, ImageExporter exporter, SaveImageInBackgroundTask(
Context context,
FeatureFlags flags,
ImageExporter exporter,
ScreenshotSmartActions screenshotSmartActions, ScreenshotSmartActions screenshotSmartActions,
ScreenshotController.SaveImageInBackgroundData data, ScreenshotController.SaveImageInBackgroundData data,
Supplier<ActionTransition> sharedElementTransition, Supplier<ActionTransition> sharedElementTransition,
@@ -92,6 +98,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
screenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider
) { ) {
mContext = context; mContext = context;
mFlags = flags;
mScreenshotSmartActions = screenshotSmartActions; mScreenshotSmartActions = screenshotSmartActions;
mImageData = new ScreenshotController.SavedImageData(); mImageData = new ScreenshotController.SavedImageData();
mQuickShareData = new ScreenshotController.QuickShareData(); mQuickShareData = new ScreenshotController.QuickShareData();
@@ -117,7 +124,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
} }
// TODO: move to constructor / from ScreenshotRequest // TODO: move to constructor / from ScreenshotRequest
final UUID requestId = UUID.randomUUID(); final UUID requestId = UUID.randomUUID();
final UserHandle user = getUserHandleOfForegroundApplication(mContext); final UserHandle user = mFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)
? mParams.owner : getUserHandleOfForegroundApplication(mContext);
Thread.currentThread().setPriority(Thread.MAX_PRIORITY); Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
@@ -133,8 +141,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Call synchronously here since already on a background thread. // Call synchronously here since already on a background thread.
ListenableFuture<ImageExporter.Result> future = ListenableFuture<ImageExporter.Result> future =
mImageExporter.export(Runnable::run, requestId, image); mImageExporter.export(Runnable::run, requestId, image, mParams.owner);
ImageExporter.Result result = future.get(); ImageExporter.Result result = future.get();
Log.d(TAG, "Saved screenshot: " + result);
final Uri uri = result.uri; final Uri uri = result.uri;
mImageTime = result.timestamp; mImageTime = result.timestamp;
@@ -157,6 +166,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
} }
mImageData.uri = uri; mImageData.uri = uri;
mImageData.owner = user;
mImageData.smartActions = smartActions; mImageData.smartActions = smartActions;
mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri);
mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri);

View File

@@ -34,6 +34,7 @@ import static java.util.Objects.requireNonNull;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.annotation.MainThread; import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityOptions; import android.app.ActivityOptions;
@@ -57,7 +58,9 @@ import android.media.AudioSystem;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
@@ -90,6 +93,7 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.ClipboardOverlayController; import com.android.systemui.clipboardoverlay.ClipboardOverlayController;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
import com.android.systemui.util.Assert; import com.android.systemui.util.Assert;
@@ -151,6 +155,7 @@ public class ScreenshotController {
public Consumer<Uri> finisher; public Consumer<Uri> finisher;
public ScreenshotController.ActionsReadyListener mActionsReadyListener; public ScreenshotController.ActionsReadyListener mActionsReadyListener;
public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener; public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener;
public UserHandle owner;
void clearImage() { void clearImage() {
image = null; image = null;
@@ -167,6 +172,8 @@ public class ScreenshotController {
public Notification.Action deleteAction; public Notification.Action deleteAction;
public List<Notification.Action> smartActions; public List<Notification.Action> smartActions;
public Notification.Action quickShareAction; public Notification.Action quickShareAction;
public UserHandle owner;
/** /**
* POD for shared element transition. * POD for shared element transition.
@@ -242,6 +249,7 @@ public class ScreenshotController {
private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000; private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
private final WindowContext mContext; private final WindowContext mContext;
private final FeatureFlags mFlags;
private final ScreenshotNotificationsController mNotificationsController; private final ScreenshotNotificationsController mNotificationsController;
private final ScreenshotSmartActions mScreenshotSmartActions; private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger; private final UiEventLogger mUiEventLogger;
@@ -288,6 +296,7 @@ public class ScreenshotController {
@Inject @Inject
ScreenshotController( ScreenshotController(
Context context, Context context,
FeatureFlags flags,
ScreenshotSmartActions screenshotSmartActions, ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController screenshotNotificationsController, ScreenshotNotificationsController screenshotNotificationsController,
ScrollCaptureClient scrollCaptureClient, ScrollCaptureClient scrollCaptureClient,
@@ -331,6 +340,7 @@ public class ScreenshotController {
final Context displayContext = context.createDisplayContext(getDefaultDisplay()); final Context displayContext = context.createDisplayContext(getDefaultDisplay());
mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null); mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
mWindowManager = mContext.getSystemService(WindowManager.class); mWindowManager = mContext.getSystemService(WindowManager.class);
mFlags = flags;
mAccessibilityManager = AccessibilityManager.getInstance(mContext); mAccessibilityManager = AccessibilityManager.getInstance(mContext);
@@ -377,7 +387,6 @@ public class ScreenshotController {
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds, void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
Insets visibleInsets, int taskId, int userId, ComponentName topComponent, Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
Consumer<Uri> finisher, RequestCallback requestCallback) { Consumer<Uri> finisher, RequestCallback requestCallback) {
// TODO: use task Id, userId, topComponent for smart handler
Assert.isMainThread(); Assert.isMainThread();
if (screenshot == null) { if (screenshot == null) {
Log.e(TAG, "Got null bitmap from screenshot message"); Log.e(TAG, "Got null bitmap from screenshot message");
@@ -395,7 +404,7 @@ public class ScreenshotController {
} }
mCurrentRequestCallback = requestCallback; mCurrentRequestCallback = requestCallback;
saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent, saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent,
showFlash); showFlash, UserHandle.of(userId));
} }
/** /**
@@ -543,14 +552,15 @@ public class ScreenshotController {
return; return;
} }
saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true); saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true,
Process.myUserHandle());
mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION), mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
ClipboardOverlayController.SELF_PERMISSION); ClipboardOverlayController.SELF_PERMISSION);
} }
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, ComponentName topComponent, boolean showFlash) { Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
withWindowAttached(() -> withWindowAttached(() ->
mScreenshotView.announceForAccessibility( mScreenshotView.announceForAccessibility(
mContext.getResources().getString(R.string.screenshot_saving_title))); mContext.getResources().getString(R.string.screenshot_saving_title)));
@@ -575,11 +585,11 @@ public class ScreenshotController {
mScreenBitmap = screenshot; mScreenBitmap = screenshot;
if (!isUserSetupComplete()) { if (!isUserSetupComplete(owner)) {
Log.w(TAG, "User setup not complete, displaying toast only"); Log.w(TAG, "User setup not complete, displaying toast only");
// User setup isn't complete, so we don't want to show any UI beyond a toast, as editing // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
// and sharing shouldn't be exposed to the user. // and sharing shouldn't be exposed to the user.
saveScreenshotAndToast(finisher); saveScreenshotAndToast(owner, finisher);
return; return;
} }
@@ -587,7 +597,7 @@ public class ScreenshotController {
mScreenBitmap.setHasAlpha(false); mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw(); mScreenBitmap.prepareToDraw();
saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady, saveScreenshotInWorkerThread(owner, finisher, this::showUiOnActionsReady,
this::showUiOnQuickShareActionReady); this::showUiOnQuickShareActionReady);
// The window is focusable by default // The window is focusable by default
@@ -853,11 +863,12 @@ public class ScreenshotController {
* Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
* failure). * failure).
*/ */
private void saveScreenshotAndToast(Consumer<Uri> finisher) { private void saveScreenshotAndToast(UserHandle owner, Consumer<Uri> finisher) {
// Play the shutter sound to notify that we've taken a screenshot // Play the shutter sound to notify that we've taken a screenshot
playCameraSound(); playCameraSound();
saveScreenshotInWorkerThread( saveScreenshotInWorkerThread(
owner,
/* onComplete */ finisher, /* onComplete */ finisher,
/* actionsReadyListener */ imageData -> { /* actionsReadyListener */ imageData -> {
if (DEBUG_CALLBACK) { if (DEBUG_CALLBACK) {
@@ -925,9 +936,11 @@ public class ScreenshotController {
/** /**
* Creates a new worker thread and saves the screenshot to the media store. * Creates a new worker thread and saves the screenshot to the media store.
*/ */
private void saveScreenshotInWorkerThread(Consumer<Uri> finisher, private void saveScreenshotInWorkerThread(
@Nullable ScreenshotController.ActionsReadyListener actionsReadyListener, UserHandle owner,
@Nullable ScreenshotController.QuickShareActionReadyListener @NonNull Consumer<Uri> finisher,
@Nullable ActionsReadyListener actionsReadyListener,
@Nullable QuickShareActionReadyListener
quickShareActionsReadyListener) { quickShareActionsReadyListener) {
ScreenshotController.SaveImageInBackgroundData ScreenshotController.SaveImageInBackgroundData
data = new ScreenshotController.SaveImageInBackgroundData(); data = new ScreenshotController.SaveImageInBackgroundData();
@@ -935,13 +948,14 @@ public class ScreenshotController {
data.finisher = finisher; data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener; data.mActionsReadyListener = actionsReadyListener;
data.mQuickShareActionsReadyListener = quickShareActionsReadyListener; data.mQuickShareActionsReadyListener = quickShareActionsReadyListener;
data.owner = owner;
if (mSaveInBgTask != null) { if (mSaveInBgTask != null) {
// just log success/failure for the pre-existing screenshot // just log success/failure for the pre-existing screenshot
mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady); mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
} }
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mFlags, mImageExporter,
mScreenshotSmartActions, data, getActionTransitionSupplier(), mScreenshotSmartActions, data, getActionTransitionSupplier(),
mScreenshotNotificationSmartActionsProvider); mScreenshotNotificationSmartActionsProvider);
mSaveInBgTask.execute(); mSaveInBgTask.execute();
@@ -960,6 +974,15 @@ public class ScreenshotController {
mScreenshotHandler.resetTimeout(); mScreenshotHandler.resetTimeout();
if (imageData.uri != null) { if (imageData.uri != null) {
if (!imageData.owner.equals(Process.myUserHandle())) {
// TODO: Handle non-primary user ownership (e.g. Work Profile)
// This image is owned by another user. Special treatment will be
// required in the UI (badging) as well as sending intents which can
// correctly forward those URIs on to be read (actions).
Log.d(TAG, "*** Screenshot saved to a non-primary user ("
+ imageData.owner + ") as " + imageData.uri);
}
mScreenshotHandler.post(() -> { mScreenshotHandler.post(() -> {
if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
@@ -1033,9 +1056,9 @@ public class ScreenshotController {
} }
} }
private boolean isUserSetupComplete() { private boolean isUserSetupComplete(UserHandle owner) {
return Settings.Secure.getInt(mContext.getContentResolver(), return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1; .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
} }
/** /**

View File

@@ -68,7 +68,9 @@ internal open class ScreenshotPolicyImpl @Inject constructor(
} }
override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean { override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean {
return withContext(bgDispatcher) { userMgr.isManagedProfile(userId) } val managed = withContext(bgDispatcher) { userMgr.isManagedProfile(userId) }
Log.d(TAG, "isManagedProfile: $managed")
return managed
} }
private fun nonPipVisibleTask(info: RootTaskInfo): Boolean { private fun nonPipVisibleTask(info: RootTaskInfo): Boolean {

View File

@@ -78,6 +78,7 @@ public class ScrollCaptureController {
static class LongScreenshot { static class LongScreenshot {
private final ImageTileSet mImageTileSet; private final ImageTileSet mImageTileSet;
private final Session mSession; private final Session mSession;
// TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy
LongScreenshot(Session session, ImageTileSet imageTileSet) { LongScreenshot(Session session, ImageTileSet imageTileSet) {
mSession = session; mSession = session;

View File

@@ -33,6 +33,7 @@ import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Build; import android.os.Build;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
@@ -97,7 +98,8 @@ public class ImageExporterTest extends SysuiTestCase {
Bitmap original = createCheckerBitmap(10, 10, 10); Bitmap original = createCheckerBitmap(10, 10, 10);
ListenableFuture<ImageExporter.Result> direct = ListenableFuture<ImageExporter.Result> direct =
exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME); exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME,
Process.myUserHandle());
assertTrue("future should be done", direct.isDone()); assertTrue("future should be done", direct.isDone());
assertFalse("future should not be canceled", direct.isCancelled()); assertFalse("future should not be canceled", direct.isCancelled());
ImageExporter.Result result = direct.get(); ImageExporter.Result result = direct.get();

View File

@@ -180,7 +180,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.finisher = null; data.finisher = null;
data.mActionsReadyListener = null; data.mActionsReadyListener = null;
SaveImageInBackgroundTask task = SaveImageInBackgroundTask task =
new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data,
ActionTransition::new, mSmartActionsProvider); ActionTransition::new, mSmartActionsProvider);
Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
@@ -208,7 +208,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.finisher = null; data.finisher = null;
data.mActionsReadyListener = null; data.mActionsReadyListener = null;
SaveImageInBackgroundTask task = SaveImageInBackgroundTask task =
new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data,
ActionTransition::new, mSmartActionsProvider); ActionTransition::new, mSmartActionsProvider);
Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
@@ -236,7 +236,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.finisher = null; data.finisher = null;
data.mActionsReadyListener = null; data.mActionsReadyListener = null;
SaveImageInBackgroundTask task = SaveImageInBackgroundTask task =
new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data,
ActionTransition::new, mSmartActionsProvider); ActionTransition::new, mSmartActionsProvider);
Notification.Action deleteAction = task.createDeleteAction(mContext, Notification.Action deleteAction = task.createDeleteAction(mContext,