summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Zak Cohen <zakcohen@google.com> 2020-05-15 14:19:08 -0700
committer Zak Cohen <zakcohen@google.com> 2020-06-10 10:39:19 -0700
commitb8b2f06d05641639c5f77dbac2886e26b2a94afd (patch)
tree269a1bf26f3242a7c185c31b1d5d4a0f9913fae8
parenta7642cd37bcecf5dbdf84194ce99d689cceeedf4 (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.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java144
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).