Merge "Simplify UI and animation choreography a bit"
This commit is contained in:
committed by
Android (Google) Code Review
commit
196adefb8f
@@ -25,7 +25,7 @@
|
||||
android:layout_marginBottom="@dimen/screenshot_offset_y"
|
||||
android:scaleType="fitEnd"
|
||||
android:elevation="@dimen/screenshot_preview_elevation"
|
||||
android:visibility="gone"
|
||||
android:visibility="invisible"
|
||||
android:background="@drawable/screenshot_rounded_corners"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/screenshot_edit_label"
|
||||
|
||||
@@ -66,6 +66,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
@@ -79,13 +80,15 @@ import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.policy.PhoneWindow;
|
||||
import com.android.settingslib.applications.InterestingConfigChanges;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
|
||||
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -172,7 +175,7 @@ public class ScreenshotController {
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
private final ImageExporter mImageExporter;
|
||||
private final Executor mMainExecutor;
|
||||
private final Executor mBgExecutor;
|
||||
private final ExecutorService mBgExecutor;
|
||||
|
||||
private final WindowManager mWindowManager;
|
||||
private final WindowManager.LayoutParams mWindowLayoutParams;
|
||||
@@ -181,7 +184,6 @@ public class ScreenshotController {
|
||||
private final ScrollCaptureClient mScrollCaptureClient;
|
||||
private final DeviceConfigProxy mConfigProxy;
|
||||
private final PhoneWindow mWindow;
|
||||
private final View mDecorView;
|
||||
private final DisplayManager mDisplayManager;
|
||||
|
||||
private ScreenshotView mScreenshotView;
|
||||
@@ -189,8 +191,7 @@ public class ScreenshotController {
|
||||
private SaveImageInBackgroundTask mSaveInBgTask;
|
||||
|
||||
private Animator mScreenshotAnimation;
|
||||
|
||||
private Runnable mOnCompleteRunnable;
|
||||
private RequestCallback mCurrentRequestCallback;
|
||||
|
||||
private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
@@ -227,15 +228,14 @@ public class ScreenshotController {
|
||||
UiEventLogger uiEventLogger,
|
||||
DeviceConfigProxy configProxy,
|
||||
ImageExporter imageExporter,
|
||||
@Main Executor mainExecutor,
|
||||
@Background Executor bgExecutor) {
|
||||
@Main Executor mainExecutor) {
|
||||
mScreenshotSmartActions = screenshotSmartActions;
|
||||
mNotificationsController = screenshotNotificationsController;
|
||||
mScrollCaptureClient = scrollCaptureClient;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mImageExporter = imageExporter;
|
||||
mMainExecutor = mainExecutor;
|
||||
mBgExecutor = bgExecutor;
|
||||
mBgExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
|
||||
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
|
||||
@@ -266,8 +266,8 @@ public class ScreenshotController {
|
||||
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
|
||||
mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
|
||||
mWindow.setBackgroundDrawableResource(android.R.color.transparent);
|
||||
mDecorView = mWindow.getDecorView();
|
||||
|
||||
mConfigChanges.applyNewConfig(context.getResources());
|
||||
reloadAssets();
|
||||
|
||||
// Setup the Camera shutter sound
|
||||
@@ -275,9 +275,8 @@ public class ScreenshotController {
|
||||
mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
|
||||
void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
|
||||
mOnCompleteRunnable = onComplete;
|
||||
|
||||
void takeScreenshotFullscreen(Consumer<Uri> finisher, RequestCallback requestCallback) {
|
||||
mCurrentRequestCallback = requestCallback;
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||
takeScreenshotInternal(
|
||||
@@ -287,16 +286,14 @@ public class ScreenshotController {
|
||||
|
||||
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
|
||||
Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
|
||||
Consumer<Uri> finisher, Runnable onComplete) {
|
||||
Consumer<Uri> finisher, RequestCallback requestCallback) {
|
||||
// TODO: use task Id, userId, topComponent for smart handler
|
||||
mOnCompleteRunnable = onComplete;
|
||||
|
||||
if (screenshot == null) {
|
||||
Log.e(TAG, "Got null bitmap from screenshot message");
|
||||
mNotificationsController.notifyScreenshotError(
|
||||
R.string.screenshot_failed_to_capture_text);
|
||||
finisher.accept(null);
|
||||
mOnCompleteRunnable.run();
|
||||
requestCallback.reportError();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,17 +303,19 @@ public class ScreenshotController {
|
||||
visibleInsets = Insets.NONE;
|
||||
screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight());
|
||||
}
|
||||
mCurrentRequestCallback = requestCallback;
|
||||
saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, showFlash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a screenshot selector
|
||||
*/
|
||||
void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
|
||||
dismissScreenshot(true);
|
||||
mOnCompleteRunnable = onComplete;
|
||||
void takeScreenshotPartial(final Consumer<Uri> finisher, RequestCallback requestCallback) {
|
||||
mScreenshotView.reset();
|
||||
mCurrentRequestCallback = requestCallback;
|
||||
|
||||
mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
|
||||
attachWindow();
|
||||
mWindow.setContentView(mScreenshotView);
|
||||
|
||||
mScreenshotView.takePartialScreenshot(
|
||||
rect -> takeScreenshotInternal(finisher, rect));
|
||||
@@ -337,9 +336,9 @@ public class ScreenshotController {
|
||||
}
|
||||
return;
|
||||
}
|
||||
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
|
||||
cancelTimeout();
|
||||
if (immediate) {
|
||||
resetScreenshotView();
|
||||
finishDismiss();
|
||||
} else {
|
||||
mScreenshotView.animateDismissal();
|
||||
}
|
||||
@@ -354,6 +353,8 @@ public class ScreenshotController {
|
||||
*/
|
||||
void releaseContext() {
|
||||
mContext.release();
|
||||
mCameraSound.release();
|
||||
mBgExecutor.shutdownNow();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,12 +364,8 @@ public class ScreenshotController {
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "reloadAssets()");
|
||||
}
|
||||
boolean wasAttached = mDecorView.isAttachedToWindow();
|
||||
if (wasAttached) {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "Removing screenshot window");
|
||||
}
|
||||
mWindowManager.removeView(mDecorView);
|
||||
if (mScreenshotView != null && mScreenshotView.isAttachedToWindow()) {
|
||||
mWindow.clearContentView(); // Is there a simpler way to say "remove screenshotView?"
|
||||
}
|
||||
|
||||
// respect the display cutout in landscape (since we'd otherwise overlap) but not portrait
|
||||
@@ -376,12 +373,6 @@ public class ScreenshotController {
|
||||
mWindowLayoutParams.setFitInsetsTypes(
|
||||
orientation == ORIENTATION_PORTRAIT ? 0 : WindowInsets.Type.displayCutout());
|
||||
|
||||
// ignore system bar insets for the purpose of window layout
|
||||
mDecorView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
|
||||
new WindowInsets.Builder(insets)
|
||||
.setInsets(WindowInsets.Type.all(), Insets.NONE)
|
||||
.build()));
|
||||
|
||||
// Inflate the screenshot layout
|
||||
mScreenshotView = (ScreenshotView)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
|
||||
@@ -393,12 +384,18 @@ public class ScreenshotController {
|
||||
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
resetScreenshotView();
|
||||
finishDismiss();
|
||||
}
|
||||
});
|
||||
|
||||
// ignore system bar insets for the purpose of window layout
|
||||
mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
|
||||
new WindowInsets.Builder(insets)
|
||||
.setInsets(WindowInsets.Type.all(), Insets.NONE)
|
||||
.build()));
|
||||
|
||||
// TODO(159460485): Remove this when focus is handled properly in the system
|
||||
mDecorView.setOnTouchListener((v, event) -> {
|
||||
mScreenshotView.setOnTouchListener((v, event) -> {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
|
||||
if (DEBUG_INPUT) {
|
||||
Log.d(TAG, "onTouch: ACTION_OUTSIDE");
|
||||
@@ -420,8 +417,10 @@ public class ScreenshotController {
|
||||
return false;
|
||||
});
|
||||
|
||||
// view is added to window manager in startAnimation
|
||||
mWindow.setContentView(mScreenshotView, mWindowLayoutParams);
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "adding OnComputeInternalInsetsListener");
|
||||
}
|
||||
mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,14 +457,9 @@ public class ScreenshotController {
|
||||
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
|
||||
mNotificationsController.notifyScreenshotError(
|
||||
R.string.screenshot_failed_to_capture_text);
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "Supplying null to Consumer<Uri>");
|
||||
if (mCurrentRequestCallback != null) {
|
||||
mCurrentRequestCallback.reportError();
|
||||
}
|
||||
finisher.accept(null);
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "Calling mOnCompleteRunnable.run()");
|
||||
}
|
||||
mOnCompleteRunnable.run();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -482,6 +476,13 @@ public class ScreenshotController {
|
||||
mAccessibilityManager.sendAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "saveScreenshot: reloading assets");
|
||||
}
|
||||
reloadAssets();
|
||||
}
|
||||
|
||||
if (mScreenshotView.isAttachedToWindow()) {
|
||||
// if we didn't already dismiss for another reason
|
||||
if (!mScreenshotView.isDismissing()) {
|
||||
@@ -508,18 +509,101 @@ public class ScreenshotController {
|
||||
mScreenBitmap.setHasAlpha(false);
|
||||
mScreenBitmap.prepareToDraw();
|
||||
|
||||
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "saveScreenshot: reloading assets");
|
||||
}
|
||||
reloadAssets();
|
||||
}
|
||||
saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
|
||||
|
||||
// The window is focusable by default
|
||||
setWindowFocusable(true);
|
||||
|
||||
// Start the post-screenshot animation
|
||||
startAnimation(finisher, screenRect, screenInsets, showFlash);
|
||||
if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
|
||||
View decorView = mWindow.getDecorView();
|
||||
|
||||
// Wait until this window is attached to request because it is
|
||||
// the reference used to locate the target window (below).
|
||||
withWindowAttached(() -> {
|
||||
mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken());
|
||||
mScrollCaptureClient.request(DEFAULT_DISPLAY,
|
||||
/* onConnection */
|
||||
(connection) -> mScreenshotHandler.post(() ->
|
||||
mScreenshotView.showScrollChip(() ->
|
||||
/* onClick */
|
||||
runScrollCapture(connection))));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
attachWindow();
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "setContentView: " + mScreenshotView);
|
||||
}
|
||||
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
|
||||
new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "onPreDraw: startAnimation");
|
||||
}
|
||||
mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
startAnimation(screenRect, showFlash);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
|
||||
setContentView(mScreenshotView);
|
||||
cancelTimeout(); // restarted after animation
|
||||
}
|
||||
|
||||
private void withWindowAttached(Runnable action) {
|
||||
View decorView = mWindow.getDecorView();
|
||||
if (decorView.isAttachedToWindow()) {
|
||||
action.run();
|
||||
} else {
|
||||
decorView.getViewTreeObserver().addOnWindowAttachListener(
|
||||
new ViewTreeObserver.OnWindowAttachListener() {
|
||||
@Override
|
||||
public void onWindowAttached() {
|
||||
decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
|
||||
action.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowDetached() { }
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void setContentView(View contentView) {
|
||||
mWindow.setContentView(contentView);
|
||||
}
|
||||
|
||||
private void attachWindow() {
|
||||
View decorView = mWindow.getDecorView();
|
||||
if (decorView.isAttachedToWindow()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "attachWindow");
|
||||
}
|
||||
mWindowManager.addView(decorView, mWindowLayoutParams);
|
||||
decorView.requestApplyInsets();
|
||||
}
|
||||
|
||||
void removeWindow() {
|
||||
final View decorView = mWindow.peekDecorView();
|
||||
if (decorView != null && decorView.isAttachedToWindow()) {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "Removing screenshot window");
|
||||
}
|
||||
mWindowManager.removeViewImmediate(decorView);
|
||||
}
|
||||
}
|
||||
|
||||
private void runScrollCapture(ScrollCaptureClient.Connection connection) {
|
||||
cancelTimeout();
|
||||
ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
|
||||
mMainExecutor, mBgExecutor, mImageExporter);
|
||||
controller.start(/* onDismiss */ () -> dismissScreenshot(false));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,11 +612,11 @@ public class ScreenshotController {
|
||||
*/
|
||||
private void saveScreenshotAndToast(Consumer<Uri> finisher) {
|
||||
// Play the shutter sound to notify that we've taken a screenshot
|
||||
mScreenshotHandler.post(() -> {
|
||||
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
});
|
||||
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
|
||||
saveScreenshotInWorkerThread(finisher, imageData -> {
|
||||
saveScreenshotInWorkerThread(
|
||||
/* onComplete */ finisher,
|
||||
/* actionsReadyListener */ imageData -> {
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri);
|
||||
}
|
||||
@@ -549,89 +633,38 @@ public class ScreenshotController {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If scrolling is enabled, check whether the current view is scrollable and if so, show the
|
||||
* scroll chip.
|
||||
*/
|
||||
private void maybeRequestScrollCapture() {
|
||||
if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
|
||||
mScrollCaptureClient.setHostWindowToken(mDecorView.getWindowToken());
|
||||
mScrollCaptureClient.request(DEFAULT_DISPLAY, (connection) ->
|
||||
mScreenshotView.showScrollChip(() -> {
|
||||
ScrollCaptureController controller = new ScrollCaptureController(
|
||||
mContext, connection, mMainExecutor, mBgExecutor, mImageExporter);
|
||||
controller.run(() -> mScreenshotHandler.post(
|
||||
() -> dismissScreenshot(false)));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation after taking the screenshot
|
||||
*/
|
||||
private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
|
||||
boolean showFlash) {
|
||||
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
|
||||
mScreenshotHandler.post(() -> {
|
||||
if (!mDecorView.isAttachedToWindow()) {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "Adding screenshot window");
|
||||
}
|
||||
mDecorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
mDecorView.removeOnAttachStateChangeListener(this);
|
||||
maybeRequestScrollCapture();
|
||||
}
|
||||
private void startAnimation(Rect screenRect, boolean showFlash) {
|
||||
if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
|
||||
mScreenshotAnimation.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
}
|
||||
});
|
||||
mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams);
|
||||
} else {
|
||||
maybeRequestScrollCapture();
|
||||
}
|
||||
mScreenshotAnimation =
|
||||
mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
|
||||
|
||||
mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets);
|
||||
// Play the shutter sound to notify that we've taken a screenshot
|
||||
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
|
||||
mScreenshotHandler.post(() -> {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "adding OnComputeInternalInsetsListener");
|
||||
}
|
||||
mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
|
||||
mScreenshotView);
|
||||
|
||||
mScreenshotAnimation =
|
||||
mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
|
||||
|
||||
saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
|
||||
|
||||
// Play the shutter sound to notify that we've taken a screenshot
|
||||
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
|
||||
if (DEBUG_ANIM) {
|
||||
Log.d(TAG, "starting post-screenshot animation");
|
||||
}
|
||||
mScreenshotAnimation.start();
|
||||
});
|
||||
});
|
||||
if (DEBUG_ANIM) {
|
||||
Log.d(TAG, "starting post-screenshot animation");
|
||||
}
|
||||
mScreenshotAnimation.start();
|
||||
}
|
||||
|
||||
/** Reset screenshot view and then call onCompleteRunnable */
|
||||
private void resetScreenshotView() {
|
||||
private void finishDismiss() {
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "resetScreenshotView");
|
||||
Log.d(TAG, "finishDismiss");
|
||||
}
|
||||
if (mScreenshotView.isAttachedToWindow()) {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "Removing screenshot window");
|
||||
}
|
||||
mWindowManager.removeViewImmediate(mDecorView);
|
||||
}
|
||||
mOnCompleteRunnable.run();
|
||||
cancelTimeout();
|
||||
removeWindow();
|
||||
mScreenshotView.reset();
|
||||
if (mCurrentRequestCallback != null) {
|
||||
mCurrentRequestCallback.onFinish();
|
||||
mCurrentRequestCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -655,8 +688,12 @@ public class ScreenshotController {
|
||||
mSaveInBgTask.execute();
|
||||
}
|
||||
|
||||
private void resetTimeout() {
|
||||
private void cancelTimeout() {
|
||||
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
|
||||
}
|
||||
|
||||
private void resetTimeout() {
|
||||
cancelTimeout();
|
||||
|
||||
AccessibilityManager accessibilityManager = (AccessibilityManager)
|
||||
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
@@ -715,7 +752,7 @@ public class ScreenshotController {
|
||||
|
||||
@Override
|
||||
public void hideSharedElements() {
|
||||
resetScreenshotView();
|
||||
finishDismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -763,13 +800,21 @@ public class ScreenshotController {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "setWindowFocusable: " + focusable);
|
||||
}
|
||||
int flags = mWindowLayoutParams.flags;
|
||||
if (focusable) {
|
||||
mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
} else {
|
||||
mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
}
|
||||
if (mDecorView.isAttachedToWindow()) {
|
||||
mWindowManager.updateViewLayout(mDecorView, mWindowLayoutParams);
|
||||
if (mWindowLayoutParams.flags == flags) {
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "setWindowFocusable: skipping, already " + focusable);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final View decorView = mWindow.peekDecorView();
|
||||
if (decorView != null && decorView.isAttachedToWindow()) {
|
||||
mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -316,21 +316,21 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mScreenshotSelectorView.requestFocus();
|
||||
}
|
||||
|
||||
void prepareForAnimation(Bitmap bitmap, Insets screenInsets) {
|
||||
void setScreenshot(Bitmap bitmap, Insets screenInsets) {
|
||||
mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
|
||||
// make static preview invisible (from gone) so we can query its location on screen
|
||||
mScreenshotPreview.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
|
||||
mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
mScreenshotPreview.buildLayer();
|
||||
if (DEBUG_ANIM) {
|
||||
Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash);
|
||||
}
|
||||
|
||||
Rect previewBounds = new Rect();
|
||||
mScreenshotPreview.getBoundsOnScreen(previewBounds);
|
||||
int[] previewLocation = new int[2];
|
||||
mScreenshotPreview.getLocationInWindow(previewLocation);
|
||||
Rect targetPosition = new Rect();
|
||||
mScreenshotPreview.getHitRect(targetPosition);
|
||||
|
||||
// ratio of preview width, end vs. start size
|
||||
float cornerScale =
|
||||
mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
|
||||
final float currentScale = 1 / cornerScale;
|
||||
@@ -358,8 +358,13 @@ public class ScreenshotView extends FrameLayout implements
|
||||
|
||||
// animate from the current location, to the static preview location
|
||||
final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
|
||||
final PointF finalPos = new PointF(previewLocation[0] + previewBounds.width() / 2f,
|
||||
previewLocation[1] + previewBounds.height() / 2f);
|
||||
final PointF finalPos = new PointF(targetPosition.exactCenterX(),
|
||||
targetPosition.exactCenterY());
|
||||
|
||||
if (DEBUG_ANIM) {
|
||||
Log.d(TAG, "toCorner: startPos=" + startPos);
|
||||
Log.d(TAG, "toCorner: finalPos=" + finalPos);
|
||||
}
|
||||
|
||||
ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
|
||||
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
|
||||
@@ -427,7 +432,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (DEBUG_ANIM) {
|
||||
Log.d(TAG, "drop-in animation completed");
|
||||
Log.d(TAG, "drop-in animation ended");
|
||||
}
|
||||
mDismissButton.setOnClickListener(view -> {
|
||||
if (DEBUG_INPUT) {
|
||||
@@ -653,13 +658,12 @@ public class ScreenshotView extends FrameLayout implements
|
||||
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
|
||||
// Clear any references to the bitmap
|
||||
mScreenshotPreview.setImageDrawable(null);
|
||||
mScreenshotPreview.setVisibility(View.INVISIBLE);
|
||||
mPendingSharedTransition = false;
|
||||
mActionsContainerBackground.setVisibility(View.GONE);
|
||||
mActionsContainer.setVisibility(View.GONE);
|
||||
mBackgroundProtection.setAlpha(0f);
|
||||
mDismissButton.setVisibility(View.GONE);
|
||||
mScreenshotPreview.setVisibility(View.GONE);
|
||||
mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
mScreenshotStatic.setTranslationX(0);
|
||||
mScreenshotPreview.setTranslationY(0);
|
||||
mScreenshotPreview.setContentDescription(
|
||||
|
||||
@@ -72,7 +72,7 @@ public class ScrollCaptureController {
|
||||
*
|
||||
* @param after action to take after the flow is complete
|
||||
*/
|
||||
public void run(final Runnable after) {
|
||||
public void start(final Runnable after) {
|
||||
mCaptureTime = ZonedDateTime.now();
|
||||
mRequestId = UUID.randomUUID();
|
||||
mConnection.start((session) -> startCapture(session, after));
|
||||
|
||||
@@ -25,6 +25,7 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
|
||||
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
|
||||
import static com.android.systemui.screenshot.LogConfig.logTag;
|
||||
|
||||
import android.annotation.MainThread;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
@@ -59,7 +60,8 @@ import javax.inject.Inject;
|
||||
public class TakeScreenshotService extends Service {
|
||||
private static final String TAG = logTag(TakeScreenshotService.class);
|
||||
|
||||
private final ScreenshotController mScreenshot;
|
||||
private ScreenshotController mScreenshot;
|
||||
|
||||
private final UserManager mUserManager;
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
private final ScreenshotNotificationsController mNotificationsController;
|
||||
@@ -79,6 +81,15 @@ public class TakeScreenshotService extends Service {
|
||||
}
|
||||
};
|
||||
|
||||
/** Informs about coarse grained state of the Controller. */
|
||||
interface RequestCallback {
|
||||
/** Respond to the current request indicating the screenshot request failed.*/
|
||||
void reportError();
|
||||
|
||||
/** The controller has completed handling this request UI has been removed */
|
||||
void onFinish();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
|
||||
UiEventLogger uiEventLogger,
|
||||
@@ -116,7 +127,8 @@ public class TakeScreenshotService extends Service {
|
||||
Log.d(TAG, "onUnbind");
|
||||
}
|
||||
if (mScreenshot != null) {
|
||||
mScreenshot.dismissScreenshot(true);
|
||||
mScreenshot.removeWindow();
|
||||
mScreenshot = null;
|
||||
}
|
||||
unregisterReceiver(mCloseSystemDialogs);
|
||||
return false;
|
||||
@@ -126,18 +138,39 @@ public class TakeScreenshotService extends Service {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mScreenshot != null) {
|
||||
mScreenshot.removeWindow();
|
||||
mScreenshot.releaseContext();
|
||||
mScreenshot = null;
|
||||
}
|
||||
if (DEBUG_SERVICE) {
|
||||
Log.d(TAG, "onDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
static class RequestCallbackImpl implements RequestCallback {
|
||||
private final Messenger mReplyTo;
|
||||
|
||||
RequestCallbackImpl(Messenger replyTo) {
|
||||
mReplyTo = replyTo;
|
||||
}
|
||||
|
||||
public void reportError() {
|
||||
reportUri(mReplyTo, null);
|
||||
sendComplete(mReplyTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
sendComplete(mReplyTo);
|
||||
}
|
||||
}
|
||||
|
||||
/** Respond to incoming Message via Binder (Messenger) */
|
||||
@MainThread
|
||||
private boolean handleMessage(Message msg) {
|
||||
final Messenger replyTo = msg.replyTo;
|
||||
final Runnable onComplete = () -> sendComplete(replyTo);
|
||||
final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri);
|
||||
RequestCallback requestCallback = new RequestCallbackImpl(replyTo);
|
||||
|
||||
// If the storage for this user is locked, we have no place to store
|
||||
// the screenshot, so skip taking it instead of showing a misleading
|
||||
@@ -146,14 +179,7 @@ public class TakeScreenshotService extends Service {
|
||||
Log.w(TAG, "Skipping screenshot because storage is locked!");
|
||||
mNotificationsController.notifyScreenshotError(
|
||||
R.string.screenshot_failed_to_save_user_locked_text);
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "handleMessage: calling uriConsumer.accept(null)");
|
||||
}
|
||||
uriConsumer.accept(null);
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "handleMessage: calling onComplete.run()");
|
||||
}
|
||||
onComplete.run();
|
||||
requestCallback.reportError();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -167,13 +193,13 @@ public class TakeScreenshotService extends Service {
|
||||
if (DEBUG_SERVICE) {
|
||||
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
|
||||
}
|
||||
mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
|
||||
mScreenshot.takeScreenshotFullscreen(uriConsumer, requestCallback);
|
||||
break;
|
||||
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
|
||||
if (DEBUG_SERVICE) {
|
||||
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
|
||||
}
|
||||
mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
|
||||
mScreenshot.takeScreenshotPartial(uriConsumer, requestCallback);
|
||||
break;
|
||||
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
|
||||
if (DEBUG_SERVICE) {
|
||||
@@ -186,8 +212,16 @@ public class TakeScreenshotService extends Service {
|
||||
int taskId = screenshotRequest.getTaskId();
|
||||
int userId = screenshotRequest.getUserId();
|
||||
ComponentName topComponent = screenshotRequest.getTopComponent();
|
||||
mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
|
||||
taskId, userId, topComponent, uriConsumer, onComplete);
|
||||
|
||||
if (screenshot == null) {
|
||||
Log.e(TAG, "Got null bitmap from screenshot message");
|
||||
mNotificationsController.notifyScreenshotError(
|
||||
R.string.screenshot_failed_to_capture_text);
|
||||
requestCallback.reportError();
|
||||
} else {
|
||||
mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
|
||||
taskId, userId, topComponent, uriConsumer, requestCallback);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "Invalid screenshot option: " + msg.what);
|
||||
@@ -196,7 +230,7 @@ public class TakeScreenshotService extends Service {
|
||||
return true;
|
||||
};
|
||||
|
||||
private void sendComplete(Messenger target) {
|
||||
private static void sendComplete(Messenger target) {
|
||||
try {
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "sendComplete: " + target);
|
||||
@@ -207,7 +241,7 @@ public class TakeScreenshotService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void reportUri(Messenger target, Uri uri) {
|
||||
private static void reportUri(Messenger target, Uri uri) {
|
||||
try {
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "reportUri: " + target + " -> " + uri);
|
||||
|
||||
Reference in New Issue
Block a user