diff options
| author | 2020-05-15 14:19:08 -0700 | |
|---|---|---|
| committer | 2020-06-10 10:39:19 -0700 | |
| commit | b8b2f06d05641639c5f77dbac2886e26b2a94afd (patch) | |
| tree | 269a1bf26f3242a7c185c31b1d5d4a0f9913fae8 | |
| parent | a7642cd37bcecf5dbdf84194ce99d689cceeedf4 (diff) | |
Screenshots - respect insets of bitmap when passed in.
Explicitly sets the size of the animated screenshot preview to the size
of the bounds for the animation provided.
Adds an option to not show the flash animation when using a provided image
to show visually consistency between the originating view and the screen
shot UI.
Bug: 154524544
Test: locally on fullscreen, partial and overview screenshots
Change-Id: I3d0b48fd44d93182472f939114025bf67bdf8e71
| -rw-r--r-- | packages/SystemUI/res/layout/global_screenshot.xml | 5 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java | 144 |
2 files changed, 117 insertions, 32 deletions
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml index fce4610b934b..6a235218b32f 100644 --- a/packages/SystemUI/res/layout/global_screenshot.xml +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -30,11 +30,10 @@ android:id="@+id/global_screenshot_animated_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" + android:layout_gravity="top|start" android:visibility="gone" android:elevation="@dimen/screenshot_preview_elevation" - android:background="@drawable/screenshot_rounded_corners" - android:adjustViewBounds="true"/> + android:background="@drawable/screenshot_rounded_corners" /> <ImageView android:id="@+id/global_screenshot_flash" android:layout_width="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index a624479fa63c..29ca0546b4bd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -41,13 +41,19 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.Insets; import android.graphics.Outline; import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.graphics.drawable.InsetDrawable; +import android.graphics.drawable.LayerDrawable; import android.media.MediaActionSound; import android.net.Uri; import android.os.Handler; @@ -449,10 +455,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset int rot = mDisplay.getRotation(); int width = crop.width(); int height = crop.height(); - takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect); + takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect, + Insets.NONE, true); } - private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) { + private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, + Insets screenInsets, boolean showFlash) { dismissScreenshot("new screenshot requested", true); mScreenBitmap = screenshot; @@ -482,7 +490,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mDismissAnimation.cancel(); } // Start the post-screenshot animation - startAnimation(finisher, mScreenBitmap.getWidth(), mScreenBitmap.getHeight(), screenRect); + startAnimation(finisher, screenRect, screenInsets, showFlash); } void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) { @@ -498,9 +506,15 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset Insets visibleInsets, int taskId, int userId, ComponentName topComponent, Consumer<Uri> finisher, Runnable onComplete) { // TODO: use task Id, userId, topComponent for smart handler - // TODO: use visibleInsets for animation + mOnCompleteRunnable = onComplete; - takeScreenshot(screenshot, finisher, screenshotScreenBounds); + if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) { + takeScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false); + } else { + takeScreenshot(screenshot, finisher, + new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE, + true); + } } /** @@ -621,8 +635,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset } // Clear any references to the bitmap - mScreenshotPreview.setImageBitmap(null); - mScreenshotAnimatedView.setImageBitmap(null); + mScreenshotPreview.setImageDrawable(null); + mScreenshotAnimatedView.setImageDrawable(null); + mScreenshotAnimatedView.setVisibility(View.GONE); mActionsContainerBackground.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); mBackgroundProtection.setAlpha(0f); @@ -688,8 +703,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset /** * Starts the animation after taking the screenshot */ - private void startAnimation( - final Consumer<Uri> finisher, int bitmapWidth, int bitmapHeight, Rect screenRect) { + private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, + boolean showFlash) { + // If power save is on, show a toast so there is some visual indication that a // screenshot has been taken. PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -701,9 +717,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset if (!mScreenshotLayout.isAttachedToWindow()) { mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); } - mScreenshotAnimatedView.setImageBitmap(mScreenBitmap); - mScreenshotPreview.setImageBitmap(mScreenBitmap); + mScreenshotAnimatedView.setImageDrawable( + createScreenDrawable(mScreenBitmap, screenInsets)); + setAnimatedViewSize(screenRect.width(), screenRect.height()); + // Show when the animation starts + mScreenshotAnimatedView.setVisibility(View.GONE); + mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets)); // make static preview invisible (from gone) so we can query its location on screen mScreenshotPreview.setVisibility(View.INVISIBLE); @@ -711,14 +731,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this); mScreenshotAnimation = - createScreenshotDropInAnimation(bitmapWidth, bitmapHeight, screenRect); + createScreenshotDropInAnimation(screenRect, showFlash); saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { - @Override - void onActionsReady(SavedImageData imageData) { - showUiOnActionsReady(imageData); - } - }); + @Override + void onActionsReady(SavedImageData imageData) { + showUiOnActionsReady(imageData); + } + }); // Play the shutter sound to notify that we've taken a screenshot mCameraSound.play(MediaActionSound.SHUTTER_CLICK); @@ -730,20 +750,17 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset }); } - private AnimatorSet createScreenshotDropInAnimation( - int bitmapWidth, int bitmapHeight, Rect bounds) { + private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) { Rect previewBounds = new Rect(); mScreenshotPreview.getBoundsOnScreen(previewBounds); - float cornerScale = mCornerSizeX / (mOrientationPortrait ? bitmapWidth : bitmapHeight); - float currentScale = bounds.height() / (float) bitmapHeight; + float cornerScale = + mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height()); + final float currentScale = 1f; mScreenshotAnimatedView.setScaleX(currentScale); mScreenshotAnimatedView.setScaleY(currentScale); - mScreenshotAnimatedView.setPivotX(0); - mScreenshotAnimatedView.setPivotY(0); - AnimatorSet dropInAnimation = new AnimatorSet(); ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1); flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS); @@ -785,13 +802,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset if (t < xPositionPct) { float xCenter = MathUtils.lerp(startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)); - mScreenshotAnimatedView.setX(xCenter - bitmapWidth * currentScaleX / 2f); + mScreenshotAnimatedView.setX(xCenter - bounds.width() * currentScaleX / 2f); } else { - mScreenshotAnimatedView.setX(finalPos.x - bitmapWidth * currentScaleX / 2f); + mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * currentScaleX / 2f); } float yCenter = MathUtils.lerp( startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)); - mScreenshotAnimatedView.setY(yCenter - bitmapHeight * currentScaleY / 2f); + mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f); }); toCorner.addListener(new AnimatorListenerAdapter() { @@ -805,8 +822,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotFlash.setAlpha(0f); mScreenshotFlash.setVisibility(View.VISIBLE); - dropInAnimation.play(flashOutAnimator).after(flashInAnimator); - dropInAnimation.play(flashOutAnimator).with(toCorner); + if (showFlash) { + dropInAnimation.play(flashOutAnimator).after(flashInAnimator); + dropInAnimation.play(flashOutAnimator).with(toCorner); + } else { + dropInAnimation.play(toCorner); + } dropInAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -972,6 +993,71 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset return animSet; } + private void setAnimatedViewSize(int width, int height) { + ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams(); + layoutParams.width = width; + layoutParams.height = height; + mScreenshotAnimatedView.setLayoutParams(layoutParams); + } + + /** Does the aspect ratio of the bitmap with insets removed match the bounds. */ + private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) { + int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right; + int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom; + + if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0 + || bitmap.getHeight() == 0) { + Log.e(TAG, String.format( + "Provided bitmap and insets create degenerate region: %dx%d %s", + bitmap.getWidth(), bitmap.getHeight(), bitmapInsets)); + return false; + } + + float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight; + float boundsAspect = ((float) screenBounds.width()) / screenBounds.height(); + + boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f; + if (!matchWithinTolerance) { + Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f", + insettedBitmapAspect, boundsAspect)); + } + + return matchWithinTolerance; + } + + /** + * Create a drawable using the size of the bitmap and insets as the fractional inset parameters. + */ + private Drawable createScreenDrawable(Bitmap bitmap, Insets insets) { + int insettedWidth = bitmap.getWidth() - insets.left - insets.right; + int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom; + + BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap); + if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0 + || bitmap.getHeight() == 0) { + Log.e(TAG, String.format( + "Can't create insetted drawable, using 0 insets " + + "bitmap and insets create degenerate region: %dx%d %s", + bitmap.getWidth(), bitmap.getHeight(), insets)); + return bitmapDrawable; + } + + InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable, + -1f * insets.left / insettedWidth, + -1f * insets.top / insettedHeight, + -1f * insets.right / insettedWidth, + -1f * insets.bottom / insettedHeight); + + if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) { + // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need + // to fill in the background of the drawable. + return new LayerDrawable(new Drawable[] { + new ColorDrawable(Color.BLACK), insetDrawable}); + } else { + return insetDrawable; + } + } + /** * Receiver to proxy the share or edit intent, used to clean up the notification and send * appropriate signals to the system (ie. to dismiss the keyguard if necessary). |