Loading packages/SystemUI/res/layout/global_screenshot.xml +2 −3 Original line number Diff line number Diff line Loading @@ -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" Loading packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +115 −29 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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); } } /** Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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); Loading @@ -726,7 +746,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this); mScreenshotAnimation = createScreenshotDropInAnimation(bitmapWidth, bitmapHeight, screenRect); createScreenshotDropInAnimation(screenRect, showFlash); saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { @Override Loading @@ -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); Loading Loading @@ -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() { Loading @@ -820,8 +837,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotFlash.setAlpha(0f); mScreenshotFlash.setVisibility(View.VISIBLE); if (showFlash) { dropInAnimation.play(flashOutAnimator).after(flashInAnimator); dropInAnimation.play(flashOutAnimator).with(toCorner); } else { dropInAnimation.play(toCorner); } dropInAnimation.addListener(new AnimatorListenerAdapter() { @Override Loading Loading @@ -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). Loading Loading
packages/SystemUI/res/layout/global_screenshot.xml +2 −3 Original line number Diff line number Diff line Loading @@ -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" Loading
packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +115 −29 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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); } } /** Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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); Loading @@ -726,7 +746,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this); mScreenshotAnimation = createScreenshotDropInAnimation(bitmapWidth, bitmapHeight, screenRect); createScreenshotDropInAnimation(screenRect, showFlash); saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { @Override Loading @@ -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); Loading Loading @@ -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() { Loading @@ -820,8 +837,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotFlash.setAlpha(0f); mScreenshotFlash.setVisibility(View.VISIBLE); if (showFlash) { dropInAnimation.play(flashOutAnimator).after(flashInAnimator); dropInAnimation.play(flashOutAnimator).with(toCorner); } else { dropInAnimation.play(toCorner); } dropInAnimation.addListener(new AnimatorListenerAdapter() { @Override Loading Loading @@ -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). Loading