From aba9743643d85e9bf5627da9d1fdc8ded25f22de Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Thu, 28 Jul 2016 17:04:04 -0700 Subject: [PATCH] Use SurfaceControl.screenshot() instead of screencap for screenshots. Change-Id: I5a9e7bbc5f3ae176ac5ae7209a133526992e92d4 Fixes: 30429392 --- .../android/shell/BugreportProgressService.java | 33 ++--- .../Shell/src/com/android/shell/Screenshooter.java | 139 +++++++++++++++++++++ 2 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 packages/Shell/src/com/android/shell/Screenshooter.java diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index f04df4b64f84..5a75f40c4036 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -60,6 +60,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.hardware.display.DisplayManagerGlobal; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -78,6 +80,8 @@ import android.text.format.DateUtils; import android.util.Log; import android.util.Patterns; import android.util.SparseArray; +import android.view.Display; +import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; import android.view.View.OnFocusChangeListener; @@ -771,7 +775,6 @@ public class BugreportProgressService extends Service { } msg = mContext.getString(R.string.bugreport_screenshot_taken); } else { - // TODO: try again using Framework APIs instead of relying on screencap. msg = mContext.getString(R.string.bugreport_screenshot_failed); Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); } @@ -1350,22 +1353,24 @@ public class BugreportProgressService extends Service { /** * Takes a screenshot and save it to the given location. */ - private static boolean takeScreenshot(Context context, String screenshotFile) { - final ProcessBuilder screencap = new ProcessBuilder() - .command("/system/bin/screencap", "-p", screenshotFile); - Log.d(TAG, "Taking screenshot using " + screencap.command()); - try { - final int exitValue = screencap.start().waitFor(); - if (exitValue == 0) { + private static boolean takeScreenshot(Context context, String path) { + final Bitmap bitmap = Screenshooter.takeScreenshot(); + if (bitmap == null) { + return false; + } + boolean status; + try (final FileOutputStream fos = new FileOutputStream(path)) { + if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) { ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(150); return true; + } else { + Log.e(TAG, "Failed to save screenshot on " + path); } - Log.e(TAG, "screencap (" + screencap.command() + ") failed: " + exitValue); - } catch (IOException e) { - Log.e(TAG, "screencap (" + screencap.command() + ") failed", e); - } catch (InterruptedException e) { - Log.w(TAG, "Thread interrupted while screencap still running"); - Thread.currentThread().interrupt(); + } catch (IOException e ) { + Log.e(TAG, "Failed to save screenshot on " + path, e); + return false; + } finally { + bitmap.recycle(); } return false; } diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java new file mode 100644 index 000000000000..3f4895b76901 --- /dev/null +++ b/packages/Shell/src/com/android/shell/Screenshooter.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.shell; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Point; +import android.hardware.display.DisplayManagerGlobal; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceControl; + +/** + * Helper class used to take screenshots. + * + * TODO: logic below was copied and pasted from UiAutomation; it should be refactored into a common + * component that could be used by both (Shell and UiAutomation). + */ +final class Screenshooter { + + private static final String TAG = "Screenshooter"; + + /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */ + public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0; + + /** Rotation constant: Freeze rotation to 90 degrees . */ + public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90; + + /** Rotation constant: Freeze rotation to 180 degrees . */ + public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180; + + /** Rotation constant: Freeze rotation to 270 degrees . */ + public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270; + + /** + * Takes a screenshot. + * + * @return The screenshot bitmap on success, null otherwise. + */ + static Bitmap takeScreenshot() { + Display display = DisplayManagerGlobal.getInstance() + .getRealDisplay(Display.DEFAULT_DISPLAY); + Point displaySize = new Point(); + display.getRealSize(displaySize); + final int displayWidth = displaySize.x; + final int displayHeight = displaySize.y; + + final float screenshotWidth; + final float screenshotHeight; + + final int rotation = display.getRotation(); + switch (rotation) { + case ROTATION_FREEZE_0: { + screenshotWidth = displayWidth; + screenshotHeight = displayHeight; + } break; + case ROTATION_FREEZE_90: { + screenshotWidth = displayHeight; + screenshotHeight = displayWidth; + } break; + case ROTATION_FREEZE_180: { + screenshotWidth = displayWidth; + screenshotHeight = displayHeight; + } break; + case ROTATION_FREEZE_270: { + screenshotWidth = displayHeight; + screenshotHeight = displayWidth; + } break; + default: { + throw new IllegalArgumentException("Invalid rotation: " + + rotation); + } + } + + Log.d(TAG, "Taking screenshot of dimensions " + displayWidth + " x " + displayHeight); + // Take the screenshot + Bitmap screenShot = + SurfaceControl.screenshot((int) screenshotWidth, (int) screenshotHeight); + if (screenShot == null) { + Log.e(TAG, "Failed to take screenshot of dimensions " + screenshotWidth + " x " + + screenshotHeight); + return null; + } + + // Rotate the screenshot to the current orientation + if (rotation != ROTATION_FREEZE_0) { + Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(unrotatedScreenShot); + canvas.translate(unrotatedScreenShot.getWidth() / 2, + unrotatedScreenShot.getHeight() / 2); + canvas.rotate(getDegreesForRotation(rotation)); + canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2); + canvas.drawBitmap(screenShot, 0, 0, null); + canvas.setBitmap(null); + screenShot.recycle(); + screenShot = unrotatedScreenShot; + } + + // Optimization + screenShot.setHasAlpha(false); + + return screenShot; + } + + private static float getDegreesForRotation(int value) { + switch (value) { + case Surface.ROTATION_90: { + return 360f - 90f; + } + case Surface.ROTATION_180: { + return 360f - 180f; + } + case Surface.ROTATION_270: { + return 360f - 270f; + } default: { + return 0; + } + } + } + +} -- 2.11.0