Merge "Keep screenshot process bound" into rvc-dev

This commit is contained in:
Miranda Kephart
2020-04-27 18:00:43 +00:00
committed by Android (Google) Code Review
4 changed files with 153 additions and 92 deletions

View File

@@ -27,6 +27,9 @@ import java.util.function.Consumer;
public class ScreenshotHelper { public class ScreenshotHelper {
public static final int SCREENSHOT_MSG_URI = 1;
public static final int SCREENSHOT_MSG_PROCESS_COMPLETE = 2;
/** /**
* Describes a screenshot request (to make it easier to pass data through to the handler). * Describes a screenshot request (to make it easier to pass data through to the handler).
*/ */
@@ -135,6 +138,7 @@ public class ScreenshotHelper {
private final int SCREENSHOT_TIMEOUT_MS = 10000; private final int SCREENSHOT_TIMEOUT_MS = 10000;
private final Object mScreenshotLock = new Object(); private final Object mScreenshotLock = new Object();
private IBinder mScreenshotService = null;
private ServiceConnection mScreenshotConnection = null; private ServiceConnection mScreenshotConnection = null;
private final Context mContext; private final Context mContext;
@@ -251,85 +255,104 @@ public class ScreenshotHelper {
private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler, private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,
ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) { ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) { synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
final ComponentName serviceComponent = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_screenshotServiceComponent));
final Intent serviceIntent = new Intent();
final Runnable mScreenshotTimeout = new Runnable() { final Runnable mScreenshotTimeout = () -> {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mScreenshotService = null;
notifyScreenshotError();
}
}
if (completionConsumer != null) {
completionConsumer.accept(null);
}
};
Message msg = Message.obtain(null, screenshotType, screenshotRequest);
final ServiceConnection myConn = mScreenshotConnection;
Handler h = new Handler(handler.getLooper()) {
@Override @Override
public void run() { public void handleMessage(Message msg) {
synchronized (mScreenshotLock) { switch (msg.what) {
if (mScreenshotConnection != null) { case SCREENSHOT_MSG_URI:
mContext.unbindService(mScreenshotConnection); if (completionConsumer != null) {
mScreenshotConnection = null; completionConsumer.accept((Uri) msg.obj);
notifyScreenshotError(); }
handler.removeCallbacks(mScreenshotTimeout);
break;
case SCREENSHOT_MSG_PROCESS_COMPLETE:
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mScreenshotService = null;
}
}
break;
}
}
};
msg.replyTo = new Messenger(h);
if (mScreenshotConnection == null) {
final ComponentName serviceComponent = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_screenshotServiceComponent));
final Intent serviceIntent = new Intent();
serviceIntent.setComponent(serviceComponent);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
mScreenshotService = service;
Messenger messenger = new Messenger(mScreenshotService);
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) {
completionConsumer.accept(null);
}
}
} }
} }
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mScreenshotService = null;
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
}
}
};
if (mContext.bindServiceAsUser(serviceIntent, conn,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
UserHandle.CURRENT)) {
mScreenshotConnection = conn;
handler.postDelayed(mScreenshotTimeout, timeoutMs);
}
} else {
Messenger messenger = new Messenger(mScreenshotService);
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) { if (completionConsumer != null) {
completionConsumer.accept(null); completionConsumer.accept(null);
} }
} }
};
serviceIntent.setComponent(serviceComponent);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, screenshotType, screenshotRequest);
final ServiceConnection myConn = this;
Handler h = new Handler(handler.getLooper()) {
@Override
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
handler.removeCallbacks(mScreenshotTimeout);
}
}
if (completionConsumer != null) {
completionConsumer.accept((Uri) msg.obj);
}
}
};
msg.replyTo = new Messenger(h);
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) {
completionConsumer.accept(null);
}
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
}
}
};
if (mContext.bindServiceAsUser(serviceIntent, conn,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
UserHandle.CURRENT)) {
mScreenshotConnection = conn;
handler.postDelayed(mScreenshotTimeout, timeoutMs); handler.postDelayed(mScreenshotTimeout, timeoutMs);
} }
} }

View File

@@ -48,7 +48,6 @@ import android.graphics.Region;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.media.MediaActionSound; import android.media.MediaActionSound;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
@@ -188,7 +187,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private final ImageView mDismissImage; private final ImageView mDismissImage;
private Bitmap mScreenBitmap; private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
private Animator mScreenshotAnimation; private Animator mScreenshotAnimation;
private Runnable mOnCompleteRunnable;
private boolean mInDarkMode = false; private boolean mInDarkMode = false;
private float mScreenshotOffsetXPx; private float mScreenshotOffsetXPx;
@@ -197,8 +198,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private float mDismissButtonSize; private float mDismissButtonSize;
private float mCornerSizeX; private float mCornerSizeX;
private AsyncTask<Void, Void, Void> mSaveInBgTask;
private MediaActionSound mCameraSound; private MediaActionSound mCameraSound;
// standard material ease // standard material ease
@@ -211,6 +210,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
case MESSAGE_CORNER_TIMEOUT: case MESSAGE_CORNER_TIMEOUT:
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT); mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
GlobalScreenshot.this.clearScreenshot("timeout"); GlobalScreenshot.this.clearScreenshot("timeout");
mOnCompleteRunnable.run();
break; break;
default: default:
break; break;
@@ -252,6 +252,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mDismissButton.setOnClickListener(view -> { mDismissButton.setOnClickListener(view -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL); mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
clearScreenshot("dismiss_button"); clearScreenshot("dismiss_button");
mOnCompleteRunnable.run();
}); });
mDismissImage = mDismissButton.findViewById(R.id.global_screenshot_dismiss_image); mDismissImage = mDismissButton.findViewById(R.id.global_screenshot_dismiss_image);
@@ -325,19 +326,19 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
data.finisher = finisher; data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener; data.mActionsReadyListener = actionsReadyListener;
data.createDeleteAction = false; data.createDeleteAction = false;
if (mSaveInBgTask != null) { if (mSaveInBgTask != null) {
mSaveInBgTask.cancel(false); mSaveInBgTask.ignoreResult();
} }
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute(); mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
mSaveInBgTask.execute();
} }
/** /**
* Takes a screenshot of the current display and shows an animation. * Takes a screenshot of the current display and shows an animation.
*/ */
private void takeScreenshot(Consumer<Uri> finisher, Rect crop) { private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
clearScreenshot("new screenshot requested");
int rot = mDisplay.getRotation(); int rot = mDisplay.getRotation();
int width = crop.width(); int width = crop.width();
int height = crop.height(); int height = crop.height();
@@ -349,10 +350,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) { private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
mScreenBitmap = screenshot; mScreenBitmap = screenshot;
if (mScreenBitmap == null) { if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError( mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text); R.string.screenshot_failed_to_capture_text);
finisher.accept(null); finisher.accept(null);
mOnCompleteRunnable.run();
return; return;
} }
@@ -366,11 +369,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this); mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
// Start the post-screenshot animation // Start the post-screenshot animation
startAnimation(finisher, screenRect.width(), screenRect.height(), startAnimation(finisher, screenRect.width(), screenRect.height(), screenRect);
screenRect);
} }
void takeScreenshot(Consumer<Uri> finisher) { void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
clearScreenshot("new screenshot requested");
mOnCompleteRunnable = onComplete;
mDisplay.getRealMetrics(mDisplayMetrics); mDisplay.getRealMetrics(mDisplayMetrics);
takeScreenshot( takeScreenshot(
finisher, finisher,
@@ -378,9 +383,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
} }
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds, void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
Insets visibleInsets, int taskId, Consumer<Uri> finisher) { Insets visibleInsets, int taskId, Consumer<Uri> finisher, Runnable onComplete) {
// TODO use taskId and visibleInsets // TODO use taskId and visibleInsets
clearScreenshot("new screenshot requested"); clearScreenshot("new screenshot requested");
mOnCompleteRunnable = onComplete;
takeScreenshot(screenshot, finisher, screenshotScreenBounds); takeScreenshot(screenshot, finisher, screenshotScreenBounds);
} }
@@ -388,7 +395,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
* Displays a screenshot selector * Displays a screenshot selector
*/ */
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
void takeScreenshotPartial(final Consumer<Uri> finisher) { void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
clearScreenshot("new screenshot requested");
mOnCompleteRunnable = onComplete;
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() { mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
@Override @Override
@@ -660,6 +670,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
() -> { () -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED); mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
clearScreenshot("chip tapped"); clearScreenshot("chip tapped");
mOnCompleteRunnable.run();
}); });
mActionsView.addView(actionChip); mActionsView.addView(actionChip);
} }
@@ -671,6 +682,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> { shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
clearScreenshot("chip tapped"); clearScreenshot("chip tapped");
mOnCompleteRunnable.run();
}); });
mActionsView.addView(shareChip); mActionsView.addView(shareChip);
@@ -681,6 +693,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
editChip.setPendingIntent(imageData.editAction.actionIntent, () -> { editChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
clearScreenshot("chip tapped"); clearScreenshot("chip tapped");
mOnCompleteRunnable.run();
}); });
mActionsView.addView(editChip); mActionsView.addView(editChip);
@@ -689,6 +702,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
imageData.editAction.actionIntent.send(); imageData.editAction.actionIntent.send();
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
clearScreenshot("screenshot preview tapped"); clearScreenshot("screenshot preview tapped");
mOnCompleteRunnable.run();
} catch (PendingIntent.CanceledException e) { } catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Intent cancelled", e); Log.e(TAG, "Intent cancelled", e);
} }

View File

@@ -230,6 +230,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
return null; return null;
} }
/**
* If we get a new screenshot request while this one is saving, we want to continue saving in
* the background but not return anything.
*/
void ignoreResult() {
mParams.mActionsReadyListener = new GlobalScreenshot.ActionsReadyListener() {
@Override
void onActionsReady(GlobalScreenshot.SavedImageData imageData) {
// do nothing
}
};
}
@Override @Override
protected void onCancelled(Void params) { protected void onCancelled(Void params) {
// If we are cancelled while the task is running in the background, we may get null // If we are cancelled while the task is running in the background, we may get null

View File

@@ -16,6 +16,9 @@
package com.android.systemui.screenshot; package com.android.systemui.screenshot;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -51,8 +54,15 @@ public class TakeScreenshotService extends Service {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
final Messenger callback = msg.replyTo; final Messenger callback = msg.replyTo;
Consumer<Uri> finisher = uri -> { Consumer<Uri> uriConsumer = uri -> {
Message reply = Message.obtain(null, 1, uri); Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);
try {
callback.send(reply);
} catch (RemoteException e) {
}
};
Runnable onComplete = () -> {
Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE);
try { try {
callback.send(reply); callback.send(reply);
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -64,7 +74,8 @@ public class TakeScreenshotService extends Service {
// animation and error notification. // animation and error notification.
if (!mUserManager.isUserUnlocked()) { if (!mUserManager.isUserUnlocked()) {
Log.w(TAG, "Skipping screenshot because storage is locked!"); Log.w(TAG, "Skipping screenshot because storage is locked!");
post(() -> finisher.accept(null)); post(() -> uriConsumer.accept(null));
post(onComplete);
return; return;
} }
@@ -79,19 +90,19 @@ public class TakeScreenshotService extends Service {
switch (msg.what) { switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (useCornerFlow) { if (useCornerFlow) {
mScreenshot.takeScreenshot(finisher); mScreenshot.takeScreenshot(uriConsumer, onComplete);
} else { } else {
mScreenshotLegacy.takeScreenshot( mScreenshotLegacy.takeScreenshot(
finisher, screenshotRequest.getHasStatusBar(), uriConsumer, screenshotRequest.getHasStatusBar(),
screenshotRequest.getHasNavBar()); screenshotRequest.getHasNavBar());
} }
break; break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
if (useCornerFlow) { if (useCornerFlow) {
mScreenshot.takeScreenshotPartial(finisher); mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
} else { } else {
mScreenshotLegacy.takeScreenshotPartial( mScreenshotLegacy.takeScreenshotPartial(
finisher, screenshotRequest.getHasStatusBar(), uriConsumer, screenshotRequest.getHasStatusBar(),
screenshotRequest.getHasNavBar()); screenshotRequest.getHasNavBar());
} }
break; break;
@@ -102,10 +113,10 @@ public class TakeScreenshotService extends Service {
int taskId = screenshotRequest.getTaskId(); int taskId = screenshotRequest.getTaskId();
if (useCornerFlow) { if (useCornerFlow) {
mScreenshot.handleImageAsScreenshot( mScreenshot.handleImageAsScreenshot(
screenshot, screenBounds, insets, taskId, finisher); screenshot, screenBounds, insets, taskId, uriConsumer, onComplete);
} else { } else {
mScreenshotLegacy.handleImageAsScreenshot( mScreenshotLegacy.handleImageAsScreenshot(
screenshot, screenBounds, insets, taskId, finisher); screenshot, screenBounds, insets, taskId, uriConsumer);
} }
break; break;
default: default: