Merge changes I4cbf9376,Ief2111ec into sc-dev

* changes:
  Fix tap-through during screenshot dismissal
  Capture a new scrim for scrolling screenshots
This commit is contained in:
Miranda Kephart
2021-06-25 11:54:26 +00:00
committed by Android (Google) Code Review
3 changed files with 102 additions and 70 deletions

View File

@@ -19,12 +19,15 @@
android:id="@+id/global_screenshot_frame" android:id="@+id/global_screenshot_frame"
android:theme="@style/Screenshot" android:theme="@style/Screenshot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:importantForAccessibility="no">
<ImageView <ImageView
android:id="@+id/screenshot_scrolling_scrim" android:id="@+id/screenshot_scrolling_scrim"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone"/> android:visibility="gone"
android:clickable="true"
android:importantForAccessibility="no"/>
<ImageView <ImageView
android:id="@+id/global_screenshot_actions_background" android:id="@+id/global_screenshot_actions_background"
android:layout_height="@dimen/screenshot_bg_protection_height" android:layout_height="@dimen/screenshot_bg_protection_height"

View File

@@ -486,9 +486,24 @@ public class ScreenshotController {
private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) { private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
// copy the input Rect, since SurfaceControl.screenshot can mutate it // copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop); Rect screenRect = new Rect(crop);
Bitmap screenshot = captureScreenshot(crop);
if (screenshot == null) {
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
if (mCurrentRequestCallback != null) {
mCurrentRequestCallback.reportError();
}
return;
}
saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
}
private Bitmap captureScreenshot(Rect crop) {
int width = crop.width(); int width = crop.width();
int height = crop.height(); int height = crop.height();
Bitmap screenshot = null; Bitmap screenshot = null;
final Display display = getDefaultDisplay(); final Display display = getDefaultDisplay();
final DisplayAddress address = display.getAddress(); final DisplayAddress address = display.getAddress();
@@ -509,18 +524,7 @@ public class ScreenshotController {
SurfaceControl.captureDisplay(captureArgs); SurfaceControl.captureDisplay(captureArgs);
screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap(); screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
} }
return screenshot;
if (screenshot == null) {
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
if (mCurrentRequestCallback != null) {
mCurrentRequestCallback.reportError();
}
return;
}
saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
} }
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
@@ -644,45 +648,56 @@ public class ScreenshotController {
final ScrollCaptureResponse response = mLastScrollCaptureResponse; final ScrollCaptureResponse response = mLastScrollCaptureResponse;
mScreenshotView.showScrollChip(/* onClick */ () -> { mScreenshotView.showScrollChip(/* onClick */ () -> {
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap); DisplayMetrics displayMetrics = new DisplayMetrics();
// Clear the reference to prevent close() in dismissScreenshot getDefaultDisplay().getRealMetrics(displayMetrics);
mLastScrollCaptureResponse = null; Bitmap newScreenshot = captureScreenshot(
final ListenableFuture<ScrollCaptureController.LongScreenshot> future = new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
mScrollCaptureController.run(response);
future.addListener(() -> {
ScrollCaptureController.LongScreenshot longScreenshot;
try {
longScreenshot = future.get();
} catch (CancellationException | InterruptedException | ExecutionException e) {
Log.e(TAG, "Exception", e);
return;
}
mLongScreenshotHolder.setLongScreenshot(longScreenshot); mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot);
mLongScreenshotHolder.setTransitionDestinationCallback( // delay starting scroll capture to make sure the scrim is up before the app moves
(transitionDestination, onTransitionEnd) -> mScreenshotView.post(() -> {
mScreenshotView.startLongScreenshotTransition( // Clear the reference to prevent close() in dismissScreenshot
transitionDestination, onTransitionEnd, mLastScrollCaptureResponse = null;
longScreenshot)); final ListenableFuture<ScrollCaptureController.LongScreenshot> future =
mScrollCaptureController.run(response);
future.addListener(() -> {
ScrollCaptureController.LongScreenshot longScreenshot;
try {
longScreenshot = future.get();
} catch (CancellationException
| InterruptedException
| ExecutionException e) {
Log.e(TAG, "Exception", e);
return;
}
final Intent intent = new Intent(mContext, LongScreenshotActivity.class); mLongScreenshotHolder.setLongScreenshot(longScreenshot);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); mLongScreenshotHolder.setTransitionDestinationCallback(
(transitionDestination, onTransitionEnd) ->
mScreenshotView.startLongScreenshotTransition(
transitionDestination, onTransitionEnd,
longScreenshot));
Pair<ActivityOptions, ExitTransitionCoordinator> transition = final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
ActivityOptions.startSharedElementAnimation(mWindow, intent.setFlags(
new ScreenshotExitTransitionCallbacksSupplier(false).get(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
null);
transition.second.startExit(); Pair<ActivityOptions, ExitTransitionCoordinator> transition =
mContext.startActivity(intent, transition.first.toBundle()); ActivityOptions.startSharedElementAnimation(mWindow,
RemoteAnimationAdapter runner = new RemoteAnimationAdapter( new ScreenshotExitTransitionCallbacksSupplier(false).get(),
SCREENSHOT_REMOTE_RUNNER, 0, 0); null);
try { transition.second.startExit();
WindowManagerGlobal.getWindowManagerService() mContext.startActivity(intent, transition.first.toBundle());
.overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY); RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
} catch (Exception e) { SCREENSHOT_REMOTE_RUNNER, 0, 0);
Log.e(TAG, "Error overriding screenshot app transition", e); try {
} WindowManagerGlobal.getWindowManagerService()
}, mMainExecutor); .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
} catch (Exception e) {
Log.e(TAG, "Error overriding screenshot app transition", e);
}
}, mMainExecutor);
});
}); });
} catch (CancellationException e) { } catch (CancellationException e) {
// Ignore // Ignore

View File

@@ -36,8 +36,10 @@ import android.app.ActivityManager;
import android.app.Notification; import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Insets; import android.graphics.Insets;
import android.graphics.Matrix; import android.graphics.Matrix;
@@ -257,10 +259,10 @@ public class ScreenshotView extends FrameLayout implements
@Override // ViewTreeObserver.OnComputeInternalInsetsListener @Override // ViewTreeObserver.OnComputeInternalInsetsListener
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
inoutInfo.touchableRegion.set(getTouchRegion()); inoutInfo.touchableRegion.set(getTouchRegion(true));
} }
private Region getTouchRegion() { private Region getTouchRegion(boolean includeScrim) {
Region touchRegion = new Region(); Region touchRegion = new Region();
final Rect tmpRect = new Rect(); final Rect tmpRect = new Rect();
@@ -273,6 +275,11 @@ public class ScreenshotView extends FrameLayout implements
mDismissButton.getBoundsOnScreen(tmpRect); mDismissButton.getBoundsOnScreen(tmpRect);
touchRegion.op(tmpRect, Region.Op.UNION); touchRegion.op(tmpRect, Region.Op.UNION);
if (includeScrim && mScrollingScrim.getVisibility() == View.VISIBLE) {
mScrollingScrim.getBoundsOnScreen(tmpRect);
touchRegion.op(tmpRect, Region.Op.UNION);
}
if (QuickStepContract.isGesturalMode(mNavMode)) { if (QuickStepContract.isGesturalMode(mNavMode)) {
final WindowManager wm = mContext.getSystemService(WindowManager.class); final WindowManager wm = mContext.getSystemService(WindowManager.class);
final WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); final WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
@@ -296,7 +303,7 @@ public class ScreenshotView extends FrameLayout implements
if (ev instanceof MotionEvent) { if (ev instanceof MotionEvent) {
MotionEvent event = (MotionEvent) ev; MotionEvent event = (MotionEvent) ev;
if (event.getActionMasked() == MotionEvent.ACTION_DOWN if (event.getActionMasked() == MotionEvent.ACTION_DOWN
&& !getTouchRegion().contains( && !getTouchRegion(false).contains(
(int) event.getRawX(), (int) event.getRawY())) { (int) event.getRawX(), (int) event.getRawY())) {
mCallbacks.onTouchOutside(); mCallbacks.onTouchOutside();
} }
@@ -313,6 +320,10 @@ public class ScreenshotView extends FrameLayout implements
@Override // ViewGroup @Override // ViewGroup
public boolean onInterceptTouchEvent(MotionEvent ev) { public boolean onInterceptTouchEvent(MotionEvent ev) {
// scrolling scrim should not be swipeable; return early if we're on the scrim
if (!getTouchRegion(false).contains((int) ev.getRawX(), (int) ev.getRawY())) {
return false;
}
// always pass through the down event so the swipe handler knows the initial state // always pass through the down event so the swipe handler knows the initial state
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
mSwipeDismissHandler.onTouch(this, ev); mSwipeDismissHandler.onTouch(this, ev);
@@ -374,10 +385,6 @@ public class ScreenshotView extends FrameLayout implements
return mScreenshotPreview; return mScreenshotPreview;
} }
View getScrollablePreview() {
return mScrollablePreview;
}
/** /**
* Set up the logger and callback on dismissal. * Set up the logger and callback on dismissal.
* *
@@ -807,8 +814,9 @@ public class ScreenshotView extends FrameLayout implements
anim.start(); anim.start();
} }
void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) { void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap,
mScrollingScrim.setImageBitmap(screenBitmap); Bitmap newBitmap) {
mScrollingScrim.setImageBitmap(newBitmap);
mScrollingScrim.setVisibility(View.VISIBLE); mScrollingScrim.setVisibility(View.VISIBLE);
Rect scrollableArea = scrollableAreaOnScreen(response); Rect scrollableArea = scrollableAreaOnScreen(response);
float scale = mCornerSizeX float scale = mCornerSizeX
@@ -828,7 +836,19 @@ public class ScreenshotView extends FrameLayout implements
mScrollablePreview.setImageBitmap(screenBitmap); mScrollablePreview.setImageBitmap(screenBitmap);
mScrollablePreview.setVisibility(View.VISIBLE); mScrollablePreview.setVisibility(View.VISIBLE);
createScreenshotFadeDismissAnimation(true).start(); mDismissButton.setVisibility(View.GONE);
mActionsContainer.setVisibility(View.GONE);
mBackgroundProtection.setVisibility(View.GONE);
// set these invisible, but not gone, so that the views are laid out correctly
mActionsContainerBackground.setVisibility(View.INVISIBLE);
mScreenshotPreviewBorder.setVisibility(View.INVISIBLE);
mScreenshotPreview.setVisibility(View.INVISIBLE);
mScrollingScrim.setImageTintBlendMode(BlendMode.SRC_ATOP);
ValueAnimator anim = ValueAnimator.ofFloat(0, .3f);
anim.addUpdateListener(animation -> mScrollingScrim.setImageTintList(
ColorStateList.valueOf(Color.argb((float) animation.getAnimatedValue(), 0, 0, 0))));
anim.setDuration(200);
anim.start();
} }
boolean isDismissing() { boolean isDismissing() {
@@ -844,10 +864,6 @@ public class ScreenshotView extends FrameLayout implements
} }
private void animateDismissal(Animator dismissAnimation) { private void animateDismissal(Animator dismissAnimation) {
if (DEBUG_WINDOW) {
Log.d(TAG, "removing OnComputeInternalInsetsListener");
}
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
mDismissAnimation = dismissAnimation; mDismissAnimation = dismissAnimation;
mDismissAnimation.addListener(new AnimatorListenerAdapter() { mDismissAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false; private boolean mCancelled = false;
@@ -909,6 +925,7 @@ public class ScreenshotView extends FrameLayout implements
mContext.getResources().getString(R.string.screenshot_preview_description)); mContext.getResources().getString(R.string.screenshot_preview_description));
mScreenshotPreview.setOnClickListener(null); mScreenshotPreview.setOnClickListener(null);
mShareChip.setOnClickListener(null); mShareChip.setOnClickListener(null);
mScrollingScrim.setVisibility(View.GONE);
mEditChip.setOnClickListener(null); mEditChip.setOnClickListener(null);
mShareChip.setIsPending(false); mShareChip.setIsPending(false);
mEditChip.setIsPending(false); mEditChip.setIsPending(false);
@@ -931,7 +948,7 @@ public class ScreenshotView extends FrameLayout implements
transition.action.actionIntent.send(); transition.action.actionIntent.send();
// fade out non-preview UI // fade out non-preview UI
createScreenshotFadeDismissAnimation(false).start(); createScreenshotFadeDismissAnimation().start();
} catch (PendingIntent.CanceledException e) { } catch (PendingIntent.CanceledException e) {
mPendingSharedTransition = false; mPendingSharedTransition = false;
if (transition.onCancelRunnable != null) { if (transition.onCancelRunnable != null) {
@@ -969,7 +986,7 @@ public class ScreenshotView extends FrameLayout implements
return animSet; return animSet;
} }
ValueAnimator createScreenshotFadeDismissAnimation(boolean fadePreview) { ValueAnimator createScreenshotFadeDismissAnimation() {
ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
alphaAnim.addUpdateListener(animation -> { alphaAnim.addUpdateListener(animation -> {
float alpha = 1 - animation.getAnimatedFraction(); float alpha = 1 - animation.getAnimatedFraction();
@@ -978,9 +995,6 @@ public class ScreenshotView extends FrameLayout implements
mActionsContainer.setAlpha(alpha); mActionsContainer.setAlpha(alpha);
mBackgroundProtection.setAlpha(alpha); mBackgroundProtection.setAlpha(alpha);
mScreenshotPreviewBorder.setAlpha(alpha); mScreenshotPreviewBorder.setAlpha(alpha);
if (fadePreview) {
mScreenshotPreview.setAlpha(alpha);
}
}); });
alphaAnim.setDuration(600); alphaAnim.setDuration(600);
return alphaAnim; return alphaAnim;