Merge "Screenshots - respect insets of bitmap when passed in." into rvc-dev am: af79a76fd5 am: 30caf1ed99

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11811308

Change-Id: I25585efc878a8d4a4ee21ddbd91a7c99b0881c49
This commit is contained in:
TreeHugger Robot
2020-06-10 23:12:49 +00:00
committed by Automerger Merge Worker
2 changed files with 117 additions and 32 deletions

View File

@@ -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"

View File

@@ -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;
@@ -461,10 +467,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;
@@ -496,7 +504,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) {
@@ -512,9 +520,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);
}
}
/**
@@ -635,8 +649,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);
@@ -703,8 +718,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);
@@ -716,9 +732,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);
@@ -726,14 +746,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);
@@ -745,20 +765,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);
@@ -800,13 +817,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() {
@@ -820,8 +837,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
@@ -987,6 +1008,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).