1.同时按下电源键+音量下键截屏

PhoneWindowManager.java

  1. private void interceptScreenshotChord() {
  2. if (mScreenshotChordEnabled
  3. && mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
  4. final long now = SystemClock.uptimeMillis();
  5. if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
  6. && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
  7. mVolumeDownKeyConsumedByScreenshotChord = true;
  8. cancelPendingPowerKeyAction();
  9.  
  10. mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());②
  11. }
  12. }
  13. }
  14.  
  15. private final Runnable mScreenshotRunnable = new Runnable() {
  16. @Override
  17. public void run() {
  18. takeScreenshot();③
  19. }
  20. };
  21.  
  22. @Override
  23. public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
  24. ...
  25. final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
  26. ...
  27. // Handle special keys.
  28. switch (keyCode) {
  29. case KeyEvent.KEYCODE_VOLUME_DOWN:
  30. case KeyEvent.KEYCODE_VOLUME_UP:
  31. case KeyEvent.KEYCODE_VOLUME_MUTE: {
  32. if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
  33. if (down) {
  34. if (isScreenOn && !mVolumeDownKeyTriggered
  35. && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
  36. mVolumeDownKeyTriggered = true;
  37. mVolumeDownKeyTime = event.getDownTime();
  38. mVolumeDownKeyConsumedByScreenshotChord = false;
  39. cancelPendingPowerKeyAction();
  40. interceptScreenshotChord();①
  41. }
  42. } else {
  43. mVolumeDownKeyTriggered = false;
  44. cancelPendingScreenshotChordAction();
  45. }
  46. ...
  47. case KeyEvent.KEYCODE_POWER: {
  48. result &= ~ACTION_PASS_TO_USER;
  49. if (down) {
  50. if (isScreenOn && !mPowerKeyTriggered
  51. && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
  52. mPowerKeyTriggered = true;
  53. mPowerKeyTime = event.getDownTime();
  54. interceptScreenshotChord();①
  55. }
  56. ...
  57.  
  58. // Assume this is called from the Handler thread.
  59. private void takeScreenshot() {
  60. synchronized (mScreenshotLock) {
  61. if (mScreenshotConnection != null) {
  62. return;
  63. }
  64. ComponentName cn = new ComponentName("com.android.systemui",
  65. "com.android.systemui.screenshot.TakeScreenshotService");④
  66. Intent intent = new Intent();
  67. intent.setComponent(cn);
  68. ServiceConnection conn = new ServiceConnection() {
  69. @Override
  70. public void onServiceConnected(ComponentName name, IBinder service) {
  71. synchronized (mScreenshotLock) {
  72. if (mScreenshotConnection != this) {
  73. return;
  74. }
  75. Messenger messenger = new Messenger(service);
  76. Message msg = Message.obtain(null, 1);
  77. final ServiceConnection myConn = this;
  78. Handler h = new Handler(mHandler.getLooper()) {
  79. @Override
  80. public void handleMessage(Message msg) {
  81. synchronized (mScreenshotLock) {
  82. if (mScreenshotConnection == myConn) {
  83. mContext.unbindService(mScreenshotConnection);
  84. mScreenshotConnection = null;
  85. mHandler.removeCallbacks(mScreenshotTimeout);
  86. }
  87. }
  88. }
  89. };
  90. msg.replyTo = new Messenger(h);
  91. msg.arg1 = msg.arg2 = 0;
  92. if (mStatusBar != null && mStatusBar.isVisibleLw())
  93. msg.arg1 = 1;
  94. if (mNavigationBar != null && mNavigationBar.isVisibleLw())
  95. msg.arg2 = 1;
  96. try {
  97. messenger.send(msg);
  98. } catch (RemoteException e) {
  99. }
  100. }
  101. }
  102. @Override
  103. public void onServiceDisconnected(ComponentName name) {}
  104. };
  105. if (mContext.bindServiceAsUser(
  106. intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
  107. mScreenshotConnection = conn;
  108. mHandler.postDelayed(mScreenshotTimeout, 10000);
  109. }
  110. }
  111. }

TakeScreenshotService.java

  1. public class TakeScreenshotService extends Service {
  2. private static final String TAG = "TakeScreenshotService";
  3.  
  4. private static GlobalScreenshot mScreenshot;
  5.  
  6. private Handler mHandler = new Handler() {
  7. @Override
  8. public void handleMessage(Message msg) {
  9. switch (msg.what) {
  10. case 1:
  11. final Messenger callback = msg.replyTo;
  12. if (mScreenshot == null) {
  13. mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
  14. }
  15. mScreenshot.takeScreenshot(new Runnable() {
  16. @Override public void run() {
  17. Message reply = Message.obtain(null, 1);
  18. try {
  19. callback.send(reply);
  20. } catch (RemoteException e) {
  21. }
  22. }
  23. }, msg.arg1 > 0, msg.arg2 > 0);
  24. }
  25. }
  26. };
  27.  
  28. @Override
  29. public IBinder onBind(Intent intent) {
  30. return new Messenger(mHandler).getBinder();
  31. }
  32. }

GlobalScreenshot.java

  1. SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
  2. NotificationManager nManager, int nId) {
  3. Resources r = context.getResources();
  4.  
  5. // Prepare all the output metadata
  6. mImageTime = System.currentTimeMillis();
  7. String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));
  8. mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
  9.  
  10. mScreenshotDir = new File(Environment.getExternalStoragePublicDirectory(
  11. Environment.DIRECTORY_PICTURES), SCREENSHOTS_DIR_NAME);
  12. mImageFilePath = new File(mScreenshotDir, mImageFileName).getAbsolutePath();④
  13.  
  14. // Create the large notification icon
  15. mImageWidth = data.image.getWidth();
  16. mImageHeight = data.image.getHeight();
  17. int iconSize = data.iconSize;
  18.  
  19. final int shortSide = mImageWidth < mImageHeight ? mImageWidth : mImageHeight;
  20. Bitmap preview = Bitmap.createBitmap(shortSide, shortSide, data.image.getConfig());
  21. Canvas c = new Canvas(preview);
  22. Paint paint = new Paint();
  23. ColorMatrix desat = new ColorMatrix();
  24. desat.setSaturation(0.25f);
  25. paint.setColorFilter(new ColorMatrixColorFilter(desat));
  26. Matrix matrix = new Matrix();
  27. matrix.postTranslate((shortSide - mImageWidth) / 2,
  28. (shortSide - mImageHeight) / 2);
  29. c.drawBitmap(data.image, matrix, paint);
  30. c.drawColor(0x40FFFFFF);
  31. c.setBitmap(null);
  32.  
  33. Bitmap croppedIcon = Bitmap.createScaledBitmap(preview, iconSize, iconSize, true);
  34.  
  35. // Show the intermediate notification
  36. mTickerAddSpace = !mTickerAddSpace;
  37. mNotificationId = nId;
  38. mNotificationManager = nManager;
  39. mNotificationBuilder = new Notification.Builder(context)
  40. .setTicker(r.getString(R.string.screenshot_saving_ticker)
  41. + (mTickerAddSpace ? "" : ""))
  42. .setContentTitle(r.getString(R.string.screenshot_saving_title))
  43. .setContentText(r.getString(R.string.screenshot_saving_text))
  44. .setSmallIcon(R.drawable.stat_notify_image)
  45. .setWhen(System.currentTimeMillis());
  46.  
  47. mNotificationStyle = new Notification.BigPictureStyle()
  48. .bigPicture(preview);
  49. mNotificationBuilder.setStyle(mNotificationStyle);
  50.  
  51. Notification n = mNotificationBuilder.build();
  52. n.flags |= Notification.FLAG_NO_CLEAR;
  53. mNotificationManager.notify(nId, n);
  54.  
  55. // On the tablet, the large icon makes the notification appear as if it is clickable (and
  56. // on small devices, the large icon is not shown) so defer showing the large icon until
  57. // we compose the final post-save notification below.
  58. mNotificationBuilder.setLargeIcon(croppedIcon);
  59. // But we still don't set it for the expanded view, allowing the smallIcon to show here.
  60. mNotificationStyle.bigLargeIcon(null);
  61. }
  62.  
  63. /**
  64. * Creates a new worker thread and saves the screenshot to the media store.
  65. */
  66. private void saveScreenshotInWorkerThread(Runnable finisher) {
  67. SaveImageInBackgroundData data = new SaveImageInBackgroundData();
  68. data.context = mContext;
  69. data.image = mScreenBitmap;
  70. data.iconSize = mNotificationIconSize;
  71. data.finisher = finisher;
  72. if (mSaveInBgTask != null) {
  73. mSaveInBgTask.cancel(false);
  74. }
  75. mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager,
  76. SCREENSHOT_NOTIFICATION_ID).execute(data);③
  77. }
  78.  
  79. /**
  80. * Takes a screenshot of the current display and shows an animation.
  81. */
  82. void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
  83. // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
  84. // only in the natural orientation of the device :!)
  85. mDisplay.getRealMetrics(mDisplayMetrics);
  86. float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
  87. float degrees = getDegreesForRotation(mDisplay.getRotation());
  88. boolean requiresRotation = (degrees > 0);
  89. if (requiresRotation) {
  90. // Get the dimensions of the device in its native orientation
  91. mDisplayMatrix.reset();
  92. mDisplayMatrix.preRotate(-degrees);
  93. mDisplayMatrix.mapPoints(dims);
  94. dims[0] = Math.abs(dims[0]);
  95. dims[1] = Math.abs(dims[1]);
  96. }
  97.  
  98. // Take the screenshot
  99. mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);①
  100. if (mScreenBitmap == null) {
  101. notifyScreenshotError(mContext, mNotificationManager);
  102. finisher.run();
  103. return;
  104. }
  105.  
  106. if (requiresRotation) {
  107. // Rotate the screenshot to the current orientation
  108. Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
  109. mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
  110. Canvas c = new Canvas(ss);
  111. c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
  112. c.rotate(degrees);
  113. c.translate(-dims[0] / 2, -dims[1] / 2);
  114. c.drawBitmap(mScreenBitmap, 0, 0, null);
  115. c.setBitmap(null);
  116. // Recycle the previous bitmap
  117. mScreenBitmap.recycle();
  118. mScreenBitmap = ss;
  119. }
  120.  
  121. // Optimizations
  122. mScreenBitmap.setHasAlpha(false);
  123. mScreenBitmap.prepareToDraw();
  124.  
  125. // Start the post-screenshot animation
  126. startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
  127. statusBarVisible, navBarVisible);
  128. }
  129.  
  130. /**
  131. * Starts the animation after taking the screenshot
  132. */
  133. private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
  134. boolean navBarVisible) {
  135. // Add the view for the animation
  136. mScreenshotView.setImageBitmap(mScreenBitmap);
  137. mScreenshotLayout.requestFocus();
  138.  
  139. // Setup the animation with the screenshot just taken
  140. if (mScreenshotAnimation != null) {
  141. mScreenshotAnimation.end();
  142. mScreenshotAnimation.removeAllListeners();
  143. }
  144.  
  145. mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
  146. ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
  147. ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
  148. statusBarVisible, navBarVisible);
  149. mScreenshotAnimation = new AnimatorSet();
  150. mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
  151. mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
  152. @Override
  153. public void onAnimationEnd(Animator animation) {
  154. // Save the screenshot once we have a bit of time now
  155. saveScreenshotInWorkerThread(finisher);②
  156. mWindowManager.removeView(mScreenshotLayout);
  157.  
  158. // Clear any references to the bitmap
  159. mScreenBitmap = null;
  160. mScreenshotView.setImageBitmap(null);
  161. }
  162. });
  163. mScreenshotLayout.post(new Runnable() {
  164. @Override
  165. public void run() {
  166. // Play the shutter sound to notify that we've taken a screenshot
  167. mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
  168.  
  169. mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
  170. mScreenshotView.buildLayer();
  171. mScreenshotAnimation.start();
  172. }
  173. });
  174. }

SurfaceControl.java

  1. /**
  2. * Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all
  3. * Surfaces in the screenshot.
  4. *
  5. * @param width The desired width of the returned bitmap; the raw
  6. * screen will be scaled down to this size.
  7. * @param height The desired height of the returned bitmap; the raw
  8. * screen will be scaled down to this size.
  9. * @return Returns a Bitmap containing the screen contents, or null
  10. * if an error occurs. Make sure to call Bitmap.recycle() as soon as
  11. * possible, once its content is not needed anymore.
  12. */
  13. public static Bitmap screenshot(int width, int height) {
  14. // TODO: should take the display as a parameter
  15. IBinder displayToken = SurfaceControl.getBuiltInDisplay(
  16. SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
  17. return nativeScreenshot(displayToken, width, height, 0, 0, true);
  18. }

android_view_SurfaceControl.cpp

  1. static void nativeScreenshot(JNIEnv* env, jclass clazz,
  2. jobject displayTokenObj, jobject surfaceObj,
  3. jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) {
  4. sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
  5. if (displayToken != NULL) {
  6. sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
  7. if (consumer != NULL) {
  8. if (allLayers) {
  9. minLayer = 0;
  10. maxLayer = -1;
  11. }
  12. ScreenshotClient::capture(
  13. displayToken, consumer->getIGraphicBufferProducer(),
  14. width, height, uint32_t(minLayer), uint32_t(maxLayer));
  15. }
  16. }
  17. }
  18.  
  19. static JNINativeMethod sSurfaceControlMethods[] = {
  20. ...
  21. {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZ)Landroid/graphics/Bitmap;",
  22. (void*)nativeScreenshotBitmap },
  23. {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;IIIIZ)V",
  24. (void*)nativeScreenshot },
  25. ...

SurfaceComposerClient.cpp

  1. status_t ScreenshotClient::capture(
  2. const sp<IBinder>& display,
  3. const sp<IGraphicBufferProducer>& producer,
  4. uint32_t reqWidth, uint32_t reqHeight,
  5. uint32_t minLayerZ, uint32_t maxLayerZ) {
  6. sp<ISurfaceComposer> s(ComposerService::getComposerService());
  7. if (s == NULL) return NO_INIT;
  8. return s->captureScreen(display, producer,
  9. reqWidth, reqHeight, minLayerZ, maxLayerZ,
  10. false);
  11. }

SurfaceFlinger.cpp

  1. status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
  2. const sp<IGraphicBufferProducer>& producer,
  3. uint32_t reqWidth, uint32_t reqHeight,
  4. uint32_t minLayerZ, uint32_t maxLayerZ,
  5. bool isCpuConsumer) {
  6.  
  7. if (CC_UNLIKELY(display == 0))
  8. return BAD_VALUE;
  9.  
  10. if (CC_UNLIKELY(producer == 0))
  11. return BAD_VALUE;
  12.  
  13. class MessageCaptureScreen : public MessageBase {
  14. SurfaceFlinger* flinger;
  15. sp<IBinder> display;
  16. sp<IGraphicBufferProducer> producer;
  17. uint32_t reqWidth, reqHeight;
  18. uint32_t minLayerZ,maxLayerZ;
  19. bool useReadPixels;
  20. status_t result;
  21. public:
  22. MessageCaptureScreen(SurfaceFlinger* flinger,
  23. const sp<IBinder>& display,
  24. const sp<IGraphicBufferProducer>& producer,
  25. uint32_t reqWidth, uint32_t reqHeight,
  26. uint32_t minLayerZ, uint32_t maxLayerZ, bool useReadPixels)
  27. : flinger(flinger), display(display), producer(producer),
  28. reqWidth(reqWidth), reqHeight(reqHeight),
  29. minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
  30. useReadPixels(useReadPixels),
  31. result(PERMISSION_DENIED)
  32. {
  33. }
  34. status_t getResult() const {
  35. return result;
  36. }
  37. virtual bool handler() {
  38. Mutex::Autolock _l(flinger->mStateLock);
  39. sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
  40. if (!useReadPixels) {
  41. result = flinger->captureScreenImplLocked(hw,
  42. producer, reqWidth, reqHeight, minLayerZ, maxLayerZ);
  43. } else {
  44. result = flinger->captureScreenImplCpuConsumerLocked(hw,
  45. producer, reqWidth, reqHeight, minLayerZ, maxLayerZ);
  46. }
  47. static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
  48. return true;
  49. }
  50. };
  51.  
  52. // make sure to process transactions before screenshots -- a transaction
  53. // might already be pending but scheduled for VSYNC; this guarantees we
  54. // will handle it before the screenshot. When VSYNC finally arrives
  55. // the scheduled transaction will be a no-op. If no transactions are
  56. // scheduled at this time, this will end-up being a no-op as well.
  57. mEventQueue.invalidateTransactionNow();
  58.  
  59. bool useReadPixels = false;
  60. if (isCpuConsumer) {
  61. bool formatSupportedBytBitmap =
  62. (mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBA_8888) ||
  63. (mEGLNativeVisualId == HAL_PIXEL_FORMAT_RGBX_8888);
  64. if (formatSupportedBytBitmap == false) {
  65. // the pixel format we have is not compatible with
  66. // Bitmap.java, which is the likely client of this API,
  67. // so we just revert to glReadPixels() in that case.
  68. useReadPixels = true;
  69. }
  70. if (mGpuToCpuSupported == false) {
  71. // When we know the GL->CPU path works, we can call
  72. // captureScreenImplLocked() directly, instead of using the
  73. // glReadPixels() workaround.
  74. useReadPixels = true;
  75. }
  76. }
  77.  
  78. // this creates a "fake" BBinder which will serve as a "fake" remote
  79. // binder to receive the marshaled calls and forward them to the
  80. // real remote (a BpGraphicBufferProducer)
  81. sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer);
  82.  
  83. // the asInterface() call below creates our "fake" BpGraphicBufferProducer
  84. // which does the marshaling work forwards to our "fake remote" above.
  85. sp<MessageBase> msg = new MessageCaptureScreen(this,
  86. display, IGraphicBufferProducer::asInterface( wrapper ),
  87. reqWidth, reqHeight, minLayerZ, maxLayerZ,
  88. useReadPixels);
  89.  
  90. status_t res = postMessageAsync(msg);
  91. if (res == NO_ERROR) {
  92. res = wrapper->waitForResponse();
  93. }
  94. return res;
  95. }
  96.  
  97. status_t SurfaceFlinger::captureScreenImplLocked(
  98. const sp<const DisplayDevice>& hw,
  99. const sp<IGraphicBufferProducer>& producer,
  100. uint32_t reqWidth, uint32_t reqHeight,
  101. uint32_t minLayerZ, uint32_t maxLayerZ)
  102. {
  103. ATRACE_CALL();
  104.  
  105. // get screen geometry
  106. const uint32_t hw_w = hw->getWidth();
  107. const uint32_t hw_h = hw->getHeight();
  108.  
  109. // if we have secure windows on this display, never allow the screen capture
  110. if (hw->getSecureLayerVisible()) {
  111. ALOGW("FB is protected: PERMISSION_DENIED");
  112. return PERMISSION_DENIED;
  113. }
  114.  
  115. if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
  116. ALOGE("size mismatch (%d, %d) > (%d, %d)",
  117. reqWidth, reqHeight, hw_w, hw_h);
  118. return BAD_VALUE;
  119. }
  120.  
  121. reqWidth = (!reqWidth) ? hw_w : reqWidth;
  122. reqHeight = (!reqHeight) ? hw_h : reqHeight;
  123.  
  124. // Create a surface to render into
  125. sp<Surface> surface = new Surface(producer);
  126. ANativeWindow* const window = surface.get();
  127.  
  128. // set the buffer size to what the user requested
  129. native_window_set_buffers_user_dimensions(window, reqWidth, reqHeight);
  130.  
  131. // and create the corresponding EGLSurface
  132. EGLSurface eglSurface = eglCreateWindowSurface(
  133. mEGLDisplay, mEGLConfig, window, NULL);
  134. if (eglSurface == EGL_NO_SURFACE) {
  135. ALOGE("captureScreenImplLocked: eglCreateWindowSurface() failed 0x%4x",
  136. eglGetError());
  137. return BAD_VALUE;
  138. }
  139.  
  140. if (!eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
  141. ALOGE("captureScreenImplLocked: eglMakeCurrent() failed 0x%4x",
  142. eglGetError());
  143. eglDestroySurface(mEGLDisplay, eglSurface);
  144. return BAD_VALUE;
  145. }
  146.  
  147. renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, false);
  148.  
  149. // and finishing things up...
  150. if (eglSwapBuffers(mEGLDisplay, eglSurface) != EGL_TRUE) {
  151. ALOGE("captureScreenImplLocked: eglSwapBuffers() failed 0x%4x",
  152. eglGetError());
  153. eglDestroySurface(mEGLDisplay, eglSurface);
  154. return BAD_VALUE;
  155. }
  156.  
  157. eglDestroySurface(mEGLDisplay, eglSurface);
  158.  
  159. return NO_ERROR;
  160. }
  161.  
  162. status_t SurfaceFlinger::captureScreenImplCpuConsumerLocked(
  163. const sp<const DisplayDevice>& hw,
  164. const sp<IGraphicBufferProducer>& producer,
  165. uint32_t reqWidth, uint32_t reqHeight,
  166. uint32_t minLayerZ, uint32_t maxLayerZ)
  167. {
  168. ATRACE_CALL();
  169.  
  170. if (!GLExtensions::getInstance().haveFramebufferObject()) {
  171. return INVALID_OPERATION;
  172. }
  173.  
  174. // get screen geometry
  175. const uint32_t hw_w = hw->getWidth();
  176. const uint32_t hw_h = hw->getHeight();
  177.  
  178. // if we have secure windows on this display, never allow the screen capture
  179. if (hw->getSecureLayerVisible()) {
  180. ALOGW("FB is protected: PERMISSION_DENIED");
  181. return PERMISSION_DENIED;
  182. }
  183.  
  184. if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
  185. ALOGE("size mismatch (%d, %d) > (%d, %d)",
  186. reqWidth, reqHeight, hw_w, hw_h);
  187. return BAD_VALUE;
  188. }
  189.  
  190. reqWidth = (!reqWidth) ? hw_w : reqWidth;
  191. reqHeight = (!reqHeight) ? hw_h : reqHeight;
  192.  
  193. GLuint tname;
  194. glGenRenderbuffersOES(1, &tname);
  195. glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
  196. glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, reqWidth, reqHeight);
  197.  
  198. // create a FBO
  199. GLuint name;
  200. glGenFramebuffersOES(1, &name);
  201. glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
  202. glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
  203. GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
  204.  
  205. GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
  206.  
  207. status_t result = NO_ERROR;
  208. if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
  209.  
  210. renderScreenImplLocked(hw, reqWidth, reqHeight, minLayerZ, maxLayerZ, true);
  211.  
  212. // Below we render the screenshot into the
  213. // CpuConsumer using glReadPixels from our FBO.
  214. // Some older drivers don't support the GL->CPU path so we
  215. // have to wrap it with a CPU->CPU path, which is what
  216. // glReadPixels essentially is.
  217.  
  218. sp<Surface> sur = new Surface(producer);
  219. ANativeWindow* window = sur.get();
  220.  
  221. if (native_window_api_connect(window, NATIVE_WINDOW_API_CPU) == NO_ERROR) {
  222. int err = 0;
  223. err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
  224. err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
  225. err |= native_window_set_usage(window,
  226. GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
  227.  
  228. if (err == NO_ERROR) {
  229. ANativeWindowBuffer* buffer;
  230. if (native_window_dequeue_buffer_and_wait(window, &buffer) == NO_ERROR) {
  231. sp<GraphicBuffer> buf = static_cast<GraphicBuffer*>(buffer);
  232. void* vaddr;
  233. if (buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &vaddr) == NO_ERROR) {
  234. glReadPixels(0, 0, buffer->stride, reqHeight,
  235. GL_RGBA, GL_UNSIGNED_BYTE, vaddr);
  236. buf->unlock();
  237. }
  238. window->queueBuffer(window, buffer, -1);
  239. }
  240. }
  241. native_window_api_disconnect(window, NATIVE_WINDOW_API_CPU);
  242. }
  243.  
  244. } else {
  245. ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES while taking screenshot");
  246. result = INVALID_OPERATION;
  247. }
  248.  
  249. // back to main framebuffer
  250. glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
  251. glDeleteRenderbuffersOES(1, &tname);
  252. glDeleteFramebuffersOES(1, &name);
  253.  
  254. DisplayDevice::setViewportAndProjection(hw);
  255.  
  256. return result;
  257. }

2.基于framebuffer显存设备/dev/graphics/fb0截屏

a.自带工具screencap:screencap.cpp

  1. int main(int argc, char** argv)
  2. {
  3. ProcessState::self()->startThreadPool();
  4.  
  5. const char* pname = argv[0];
  6. bool png = false;
  7. int32_t displayId = DEFAULT_DISPLAY_ID;
  8. int c;
  9. while ((c = getopt(argc, argv, "phd:")) != -1) {
  10. switch (c) {
  11. case 'p':
  12. png = true;
  13. break;
  14. case 'd':
  15. displayId = atoi(optarg);
  16. break;
  17. case '?':
  18. case 'h':
  19. usage(pname);
  20. return 1;
  21. }
  22. }
  23. argc -= optind;
  24. argv += optind;
  25.  
  26. int fd = -1;
  27. if (argc == 0) {
  28. fd = dup(STDOUT_FILENO);
  29. } else if (argc == 1) {
  30. const char* fn = argv[0];
  31. fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
  32. if (fd == -1) {
  33. fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
  34. return 1;
  35. }
  36. const int len = strlen(fn);
  37. if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
  38. png = true;
  39. }
  40. }
  41.  
  42. if (fd == -1) {
  43. usage(pname);
  44. return 1;
  45. }
  46.  
  47. void const* mapbase = MAP_FAILED;
  48. ssize_t mapsize = -1;
  49.  
  50. void const* base = 0;
  51. uint32_t w, s, h, f;
  52. size_t size = 0;
  53.  
  54. ScreenshotClient screenshot;
  55. sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
  56. if (display != NULL && screenshot.update(display) == NO_ERROR) {
  57. base = screenshot.getPixels();
  58. w = screenshot.getWidth();
  59. h = screenshot.getHeight();
  60. s = screenshot.getStride();
  61. f = screenshot.getFormat();
  62. size = screenshot.getSize();
  63. } else {
  64. const char* fbpath = "/dev/graphics/fb0";
  65. int fb = open(fbpath, O_RDONLY);
  66. if (fb >= 0) {
  67. struct fb_var_screeninfo vinfo;
  68. if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
  69. uint32_t bytespp;
  70. if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
  71. size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp;
  72. w = vinfo.xres;
  73. h = vinfo.yres;
  74. s = vinfo.xres;
  75. size = w*h*bytespp;
  76. mapsize = offset + size;
  77. mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
  78. if (mapbase != MAP_FAILED) {
  79. base = (void const *)((char const *)mapbase + offset);
  80. }
  81. }
  82. }
  83. close(fb);
  84. }
  85. }
  86.  
  87. if (base) {
  88. if (png) {
  89. SkBitmap b;
  90. b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
  91. b.setPixels((void*)base);
  92. SkDynamicMemoryWStream stream;
  93. SkImageEncoder::EncodeStream(&stream, b,
  94. SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
  95. SkData* streamData = stream.copyToData();
  96. write(fd, streamData->data(), streamData->size());
  97. streamData->unref();
  98. } else {
  99. write(fd, &w, 4);
  100. write(fd, &h, 4);
  101. write(fd, &f, 4);
  102. size_t Bpp = bytesPerPixel(f);
  103. for (size_t y=0 ; y<h ; y++) {
  104. write(fd, base, w*Bpp);
  105. base = (void *)((char *)base + s*Bpp);
  106. }
  107. }
  108. }
  109. close(fd);
  110. if (mapbase != MAP_FAILED) {
  111. munmap((void *)mapbase, mapsize);
  112. }
  113. return 0;
  114. }

b.DDMS

ScreenShotDialog.java

  1. /**
  2. * Captures a new image from the device, and display it.
  3. */
  4. private void updateDeviceImage(Shell shell) {
  5. mBusyLabel.setText("Capturing..."); // no effect
  6.  
  7. shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT));
  8.  
  9. mRawImage = getDeviceImage();①
  10.  
  11. updateImageDisplay(shell);
  12. }
  13.  
  14. /**
  15. * Updates the display with {@link #mRawImage}.
  16. * @param shell
  17. */
  18. private void updateImageDisplay(Shell shell) {
  19. Image image;
  20. if (mRawImage == null) {
  21. Display display = shell.getDisplay();
  22. image = ImageLoader.createPlaceHolderArt(
  23. display, 320, 240, display.getSystemColor(SWT.COLOR_BLUE));
  24.  
  25. mSave.setEnabled(false);
  26. mBusyLabel.setText("Screen not available");
  27. } else {
  28. // convert raw data to an Image.
  29. PaletteData palette = new PaletteData(
  30. mRawImage.getRedMask(),
  31. mRawImage.getGreenMask(),
  32. mRawImage.getBlueMask());
  33.  
  34. ImageData imageData = new ImageData(mRawImage.width, mRawImage.height,
  35. mRawImage.bpp, palette, 1, mRawImage.data);
  36. image = new Image(getParent().getDisplay(), imageData);
  37.  
  38. mSave.setEnabled(true);
  39. mBusyLabel.setText("Captured image:");
  40. }
  41.  
  42. mImageLabel.setImage(image);
  43. mImageLabel.pack();
  44. shell.pack();
  45.  
  46. // there's no way to restore old cursor; assume it's ARROW
  47. shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
  48. }
  49.  
  50. /**
  51. * Grabs an image from an ADB-connected device and returns it as a {@link RawImage}.
  52. */
  53. private RawImage getDeviceImage() {
  54. try {
  55. return mDevice.getScreenshot();②
  56. }
  57. catch (IOException ioe) {
  58. Log.w("ddms", "Unable to get frame buffer: " + ioe.getMessage());
  59. return null;
  60. } catch (TimeoutException e) {
  61. Log.w("ddms", "Unable to get frame buffer: timeout ");
  62. return null;
  63. } catch (AdbCommandRejectedException e) {
  64. Log.w("ddms", "Unable to get frame buffer: " + e.getMessage());
  65. return null;
  66. }
  67. }

Device.java

  1. @Override
  2. public RawImage getScreenshot()
  3. throws TimeoutException, AdbCommandRejectedException, IOException {
  4. return AdbHelper.getFrameBuffer(AndroidDebugBridge.getSocketAddress(), this);③
  5. }

AdbHelper.java

  1. /**
  2. * Retrieve the frame buffer from the device.
  3. * @throws TimeoutException in case of timeout on the connection.
  4. * @throws AdbCommandRejectedException if adb rejects the command
  5. * @throws IOException in case of I/O error on the connection.
  6. */
  7. static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device)
  8. throws TimeoutException, AdbCommandRejectedException, IOException {
  9.  
  10. RawImage imageParams = new RawImage();
  11. byte[] request = formAdbRequest("framebuffer:");④ //$NON-NLS-1$④
  12. byte[] nudge = {
  13. 0
  14. };
  15. byte[] reply;
  16.  
  17. SocketChannel adbChan = null;
  18. try {
  19. adbChan = SocketChannel.open(adbSockAddr);
  20. adbChan.configureBlocking(false);
  21.  
  22. // if the device is not -1, then we first tell adb we're looking to talk
  23. // to a specific device
  24. setDevice(adbChan, device);
  25.  
  26. write(adbChan, request);
  27.  
  28. AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
  29. if (!resp.okay) {
  30. throw new AdbCommandRejectedException(resp.message);
  31. }
  32.  
  33. // first the protocol version.
  34. reply = new byte[4];
  35. read(adbChan, reply);
  36.  
  37. ByteBuffer buf = ByteBuffer.wrap(reply);
  38. buf.order(ByteOrder.LITTLE_ENDIAN);
  39.  
  40. int version = buf.getInt();
  41.  
  42. // get the header size (this is a count of int)
  43. int headerSize = RawImage.getHeaderSize(version);
  44.  
  45. // read the header
  46. reply = new byte[headerSize * 4];
  47. read(adbChan, reply);
  48.  
  49. buf = ByteBuffer.wrap(reply);
  50. buf.order(ByteOrder.LITTLE_ENDIAN);
  51.  
  52. // fill the RawImage with the header
  53. if (!imageParams.readHeader(version, buf)) {
  54. Log.e("Screenshot", "Unsupported protocol: " + version);
  55. return null;
  56. }
  57.  
  58. Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="
  59. + imageParams.size + ", width=" + imageParams.width
  60. + ", height=" + imageParams.height);
  61.  
  62. write(adbChan, nudge);
  63.  
  64. reply = new byte[imageParams.size];
  65. read(adbChan, reply);
  66.  
  67. imageParams.data = reply;
  68. } finally {
  69. if (adbChan != null) {
  70. adbChan.close();
  71. }
  72. }
  73.  
  74. return imageParams;
  75. }

services.c

  1. static int create_service_thread(void (*func)(int, void *), void *cookie)
  2. {
  3. stinfo *sti;
  4. adb_thread_t t;
  5. int s[2];
  6.  
  7. if(adb_socketpair(s)) {
  8. printf("cannot create service socket pair\n");
  9. return -1;
  10. }
  11.  
  12. sti = malloc(sizeof(stinfo));
  13. if(sti == 0) fatal("cannot allocate stinfo");
  14. sti->func = func;
  15. sti->cookie = cookie;
  16. sti->fd = s[1];
  17.  
  18. if(adb_thread_create( &t, service_bootstrap_func, sti)){
  19. free(sti);
  20. adb_close(s[0]);
  21. adb_close(s[1]);
  22. printf("cannot create service thread\n");
  23. return -1;
  24. }
  25.  
  26. D("service thread started, %d:%d\n",s[0], s[1]);
  27. return s[0];
  28. }
  29.  
  30. int service_to_fd(const char *name)
  31. {
  32. ...
  33. else if(!strncmp(name, "framebuffer:", 12)) {
  34. ret = create_service_thread(framebuffer_service, 0);⑤
  35. ...

sysdeps.h

  1. static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
  2. {
  3. thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
  4. if (thread->tid == (unsigned)-1L) {
  5. return -1;
  6. }
  7. return 0;
  8. }

framebuffer_service.c

  1. void framebuffer_service(int fd, void *cookie)
  2. {
  3. struct fbinfo fbinfo;
  4. unsigned int i;
  5. char buf[640];
  6. int fd_screencap;
  7. int w, h, f;
  8. int fds[2];
  9.  
  10. if (pipe(fds) < 0) goto done;
  11.  
  12. pid_t pid = fork();
  13. if (pid < 0) goto done;
  14.  
  15. if (pid == 0) {
  16. dup2(fds[1], STDOUT_FILENO);
  17. close(fds[0]);
  18. close(fds[1]);
  19. const char* command = "screencap";⑥
  20. const char *args[2] = {command, NULL};
  21. execvp(command, (char**)args);
  22. exit(1);
  23. }
  24.  
  25. fd_screencap = fds[0];
  26.  
  27. /* read w, h & format */
  28. if(readx(fd_screencap, &w, 4)) goto done;
  29. if(readx(fd_screencap, &h, 4)) goto done;
  30. if(readx(fd_screencap, &f, 4)) goto done;
  31.  
  32. fbinfo.version = DDMS_RAWIMAGE_VERSION;
  33. /* see hardware/hardware.h */
  34. switch (f) {
  35. case 1: /* RGBA_8888 */
  36. fbinfo.bpp = 32;
  37. fbinfo.size = w * h * 4;
  38. fbinfo.width = w;
  39. fbinfo.height = h;
  40. fbinfo.red_offset = 0;
  41. fbinfo.red_length = 8;
  42. fbinfo.green_offset = 8;
  43. fbinfo.green_length = 8;
  44. fbinfo.blue_offset = 16;
  45. fbinfo.blue_length = 8;
  46. fbinfo.alpha_offset = 24;
  47. fbinfo.alpha_length = 8;
  48. break;
  49. case 2: /* RGBX_8888 */
  50. fbinfo.bpp = 32;
  51. fbinfo.size = w * h * 4;
  52. fbinfo.width = w;
  53. fbinfo.height = h;
  54. fbinfo.red_offset = 0;
  55. fbinfo.red_length = 8;
  56. fbinfo.green_offset = 8;
  57. fbinfo.green_length = 8;
  58. fbinfo.blue_offset = 16;
  59. fbinfo.blue_length = 8;
  60. fbinfo.alpha_offset = 24;
  61. fbinfo.alpha_length = 0;
  62. break;
  63. case 3: /* RGB_888 */
  64. fbinfo.bpp = 24;
  65. fbinfo.size = w * h * 3;
  66. fbinfo.width = w;
  67. fbinfo.height = h;
  68. fbinfo.red_offset = 0;
  69. fbinfo.red_length = 8;
  70. fbinfo.green_offset = 8;
  71. fbinfo.green_length = 8;
  72. fbinfo.blue_offset = 16;
  73. fbinfo.blue_length = 8;
  74. fbinfo.alpha_offset = 24;
  75. fbinfo.alpha_length = 0;
  76. break;
  77. case 4: /* RGB_565 */
  78. fbinfo.bpp = 16;
  79. fbinfo.size = w * h * 2;
  80. fbinfo.width = w;
  81. fbinfo.height = h;
  82. fbinfo.red_offset = 11;
  83. fbinfo.red_length = 5;
  84. fbinfo.green_offset = 5;
  85. fbinfo.green_length = 6;
  86. fbinfo.blue_offset = 0;
  87. fbinfo.blue_length = 5;
  88. fbinfo.alpha_offset = 0;
  89. fbinfo.alpha_length = 0;
  90. break;
  91. case 5: /* BGRA_8888 */
  92. fbinfo.bpp = 32;
  93. fbinfo.size = w * h * 4;
  94. fbinfo.width = w;
  95. fbinfo.height = h;
  96. fbinfo.red_offset = 16;
  97. fbinfo.red_length = 8;
  98. fbinfo.green_offset = 8;
  99. fbinfo.green_length = 8;
  100. fbinfo.blue_offset = 0;
  101. fbinfo.blue_length = 8;
  102. fbinfo.alpha_offset = 24;
  103. fbinfo.alpha_length = 8;
  104. break;
  105. default:
  106. goto done;
  107. }
  108.  
  109. /* write header */
  110. if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
  111.  
  112. /* write data */
  113. for(i = 0; i < fbinfo.size; i += sizeof(buf)) {
  114. if(readx(fd_screencap, buf, sizeof(buf))) goto done;
  115. if(writex(fd, buf, sizeof(buf))) goto done;
  116. }
  117. if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done;
  118. if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done;
  119.  
  120. done:
  121. TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
  122.  
  123. close(fds[0]);
  124. close(fds[1]);
  125. close(fd);
  126. }

c.screenshot2:Screenshot.java

  1. /*
  2. * Grab an image from an ADB-connected device.
  3. */
  4. private static void getDeviceImage(IDevice device, String filepath, boolean landscape)
  5. throws IOException {
  6. RawImage rawImage;
  7.  
  8. try {
  9. rawImage = device.getScreenshot();
  10. } catch (TimeoutException e) {
  11. printAndExit("Unable to get frame buffer: timeout", true /* terminate */);
  12. return;
  13. } catch (Exception ioe) {
  14. printAndExit("Unable to get frame buffer: " + ioe.getMessage(), true /* terminate */);
  15. return;
  16. }
  17.  
  18. // device/adb not available?
  19. if (rawImage == null)
  20. return;
  21.  
  22. if (landscape) {
  23. rawImage = rawImage.getRotated();
  24. }
  25.  
  26. // convert raw data to an Image
  27. BufferedImage image = new BufferedImage(rawImage.width, rawImage.height,
  28. BufferedImage.TYPE_INT_ARGB);
  29.  
  30. int index = 0;
  31. int IndexInc = rawImage.bpp >> 3;
  32. for (int y = 0 ; y < rawImage.height ; y++) {
  33. for (int x = 0 ; x < rawImage.width ; x++) {
  34. int value = rawImage.getARGB(index);
  35. index += IndexInc;
  36. image.setRGB(x, y, value);
  37. }
  38. }
  39.  
  40. if (!ImageIO.write(image, "png", new File(filepath))) {
  41. throw new IOException("Failed to find png writer");
  42. }
  43. }

3.robotiumScreenshotTaker.java

  1. /**
  2. * Takes a screenshot and saves it in "/sdcard/Robotium-Screenshots/".
  3. * Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.
  4. *
  5. * @param view the view to take screenshot of
  6. * @param name the name to give the screenshot image
  7. * @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality).
  8. */
  9. public void takeScreenshot(final String name, final int quality) {
  10. View decorView = getScreenshotView();
  11. if(decorView == null)
  12. return;
  13.  
  14. initScreenShotSaver();
  15. ScreenshotRunnable runnable = new ScreenshotRunnable(decorView, name, quality);
  16. activityUtils.getCurrentActivity(false).runOnUiThread(runnable);
  17. }
  18.  
  19. /**
  20. * Gets the proper view to use for a screenshot.
  21. */
  22. private View getScreenshotView() {
  23. View decorView = viewFetcher.getRecentDecorView(viewFetcher.getWindowDecorViews());
  24. final long endTime = SystemClock.uptimeMillis() + Timeout.getSmallTimeout();
  25.  
  26. while (decorView == null) {
  27.  
  28. final boolean timedOut = SystemClock.uptimeMillis() > endTime;
  29.  
  30. if (timedOut){
  31. return null;
  32. }
  33. sleeper.sleepMini();
  34. decorView = viewFetcher.getRecentDecorView(viewFetcher.getWindowDecorViews());
  35. }
  36. wrapAllGLViews(decorView);
  37.  
  38. return decorView;
  39. }
  40.  
  41. /**
  42. * Extract and wrap the all OpenGL ES Renderer.
  43. */
  44. private void wrapAllGLViews(View decorView) {
  45. ArrayList<GLSurfaceView> currentViews = viewFetcher.getCurrentViews(GLSurfaceView.class, decorView);
  46. final CountDownLatch latch = new CountDownLatch(currentViews.size());
  47.  
  48. for (GLSurfaceView glView : currentViews) {
  49. Object renderContainer = new Reflect(glView).field("mGLThread")
  50. .type(GLSurfaceView.class).out(Object.class);
  51.  
  52. Renderer renderer = new Reflect(renderContainer).field("mRenderer").out(Renderer.class);
  53.  
  54. if (renderer == null) {
  55. renderer = new Reflect(glView).field("mRenderer").out(Renderer.class);
  56. renderContainer = glView;
  57. }
  58. if (renderer == null) {
  59. latch.countDown();
  60. continue;
  61. }
  62. if (renderer instanceof GLRenderWrapper) {
  63. GLRenderWrapper wrapper = (GLRenderWrapper) renderer;
  64. wrapper.setTakeScreenshot();
  65. wrapper.setLatch(latch);
  66. } else {
  67. GLRenderWrapper wrapper = new GLRenderWrapper(glView, renderer, latch);
  68. new Reflect(renderContainer).field("mRenderer").in(wrapper);
  69. }
  70. }
  71.  
  72. try {
  73. latch.await();
  74. } catch (InterruptedException ex) {
  75. ex.printStackTrace();
  76. }
  77. }
  78.  
  79. /**
  80. * Returns a bitmap of a given WebView.
  81. *
  82. * @param webView the webView to save a bitmap from
  83. * @return a bitmap of the given web view
  84. *
  85. */
  86.  
  87. private Bitmap getBitmapOfWebView(final WebView webView){
  88. Picture picture = webView.capturePicture();
  89. Bitmap b = Bitmap.createBitmap( picture.getWidth(), picture.getHeight(), Bitmap.Config.ARGB_8888);
  90. Canvas c = new Canvas(b);
  91. picture.draw(c);
  92. return b;
  93. }
  94.  
  95. /**
  96. * Returns a bitmap of a given View.
  97. *
  98. * @param view the view to save a bitmap from
  99. * @return a bitmap of the given view
  100. *
  101. */
  102.  
  103. private Bitmap getBitmapOfView(final View view){
  104. view.destroyDrawingCache();
  105. view.buildDrawingCache(false);
  106. Bitmap orig = view.getDrawingCache();
  107. Bitmap.Config config = null;
  108.  
  109. if(orig != null) {
  110. config = orig.getConfig();
  111. }
  112.  
  113. if(config == null) {
  114. config = Bitmap.Config.ARGB_8888;
  115. }
  116. Bitmap b = orig.copy(config, false);
  117. view.destroyDrawingCache();
  118. return b;
  119. }
  120.  
  121. /**
  122. * Here we have a Runnable which is responsible for taking the actual screenshot,
  123. * and then posting the bitmap to a Handler which will save it.
  124. *
  125. * This Runnable is run on the UI thread.
  126. */
  127. private class ScreenshotRunnable implements Runnable {
  128.  
  129. private View view;
  130. private String name;
  131. private int quality;
  132.  
  133. public ScreenshotRunnable(final View _view, final String _name, final int _quality) {
  134. view = _view;
  135. name = _name;
  136. quality = _quality;
  137. }
  138.  
  139. public void run() {
  140. if(view !=null){
  141. Bitmap b;
  142.  
  143. if(view instanceof WebView){
  144. b = getBitmapOfWebView((WebView) view);
  145. }
  146. else{
  147. b = getBitmapOfView(view);
  148. }
  149. if(b != null)
  150. screenShotSaver.saveBitmap(b, name, quality);
  151. else
  152. Log.d(LOG_TAG, "NULL BITMAP!!");
  153. }
  154. }
  155. }
  156.  
  157. /**
  158. * This class is a Handler which deals with saving the screenshots on a separate thread.
  159. *
  160. * The screenshot logic by necessity has to run on the ui thread. However, in practice
  161. * it seems that saving a screenshot (with quality 100) takes approx twice as long
  162. * as taking it in the first place.
  163. *
  164. * Saving the screenshots in a separate thread like this will thus make the screenshot
  165. * process approx 3x faster as far as the main thread is concerned.
  166. *
  167. */
  168. private class ScreenShotSaver extends Handler {
  169. public ScreenShotSaver(HandlerThread thread) {
  170. super(thread.getLooper());
  171. }
  172.  
  173. /**
  174. * This method posts a Bitmap with meta-data to the Handler queue.
  175. *
  176. * @param bitmap the bitmap to save
  177. * @param name the name of the file
  178. * @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality).
  179. */
  180. public void saveBitmap(Bitmap bitmap, String name, int quality) {
  181. Message message = this.obtainMessage();
  182. message.arg1 = quality;
  183. message.obj = bitmap;
  184. message.getData().putString("name", name);
  185. this.sendMessage(message);
  186. }
  187.  
  188. /**
  189. * Here we process the Handler queue and save the bitmaps.
  190. *
  191. * @param message A Message containing the bitmap to save, and some metadata.
  192. */
  193. public void handleMessage(Message message) {
  194. String name = message.getData().getString("name");
  195. int quality = message.arg1;
  196. Bitmap b = (Bitmap)message.obj;
  197. if(b != null) {
  198. saveFile(name, b, quality);
  199. b.recycle();
  200. }
  201. else {
  202. Log.d(LOG_TAG, "NULL BITMAP!!");
  203. }
  204. }
  205.  
  206. /**
  207. * Saves a file.
  208. *
  209. * @param name the name of the file
  210. * @param b the bitmap to save
  211. * @param quality the compression rate. From 0 (compress for lowest size) to 100 (compress for maximum quality).
  212. *
  213. */
  214. private void saveFile(String name, Bitmap b, int quality){
  215. FileOutputStream fos = null;
  216. String fileName = getFileName(name);
  217.  
  218. File directory = new File(Environment.getExternalStorageDirectory() + "/Robotium-Screenshots/");
  219. directory.mkdir();
  220.  
  221. File fileToSave = new File(directory,fileName);
  222. try {
  223. fos = new FileOutputStream(fileToSave);
  224. if (b.compress(Bitmap.CompressFormat.JPEG, quality, fos) == false)
  225. Log.d(LOG_TAG, "Compress/Write failed");
  226. fos.flush();
  227. fos.close();
  228. } catch (Exception e) {
  229. Log.d(LOG_TAG, "Can't save the screenshot! Requires write permission (android.permission.WRITE_EXTERNAL_STORAGE) in AndroidManifest.xml of the application under test.");
  230. e.printStackTrace();
  231. }
  232. }
  233. }

GLRenderWrapper.java

WebViewClassic.java

  1. @Override
  2. public Picture capturePicture() {
  3. if (mNativeClass == 0) return null;
  4. Picture result = new Picture();
  5. nativeCopyBaseContentToPicture(result);
  6. return result;
  7. }

WebView.cpp

  1. static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
  2. {
  3. SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
  4. GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture);
  5. }
  6.  
  7. void copyBaseContentToPicture(SkPicture* picture)
  8. {
  9. if (!m_baseLayer || !m_baseLayer->content())
  10. return;
  11. LayerContent* content = m_baseLayer->content();
  12. SkCanvas* canvas = picture->beginRecording(content->width(), content->height(),
  13. SkPicture::kUsePathBoundsForClip_RecordingFlag);
  14.  
  15. // clear the BaseLayerAndroid's previous matrix (set at each draw)
  16. SkMatrix baseMatrix;
  17. baseMatrix.reset();
  18. m_baseLayer->setMatrix(baseMatrix);
  19.  
  20. m_baseLayer->draw(canvas, 0);
  21.  
  22. picture->endRecording();
  23. }

Graphics.cpp

  1. SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
  2. {
  3. SkASSERT(env);
  4. SkASSERT(picture);
  5. SkASSERT(env->IsInstanceOf(picture, gPicture_class));
  6. SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID);
  7. SkASSERT(p);
  8. return p;
  9. }

CafeSnapshotHelper.java使用了除wrapper外相同的调用方法;

同样还有去除状态栏给Android设备屏幕截图

4.UiDevice.takeScreenshot (File storePath)/takeScreenshot (File storePath, float scale, int quality)

UiDevice.java

  1. /**
  2. * Take a screenshot of current window and store it as PNG
  3. *
  4. * Default scale of 1.0f (original size) and 90% quality is used
  5. * The screenshot is adjusted per screen rotation
  6. *
  7. * @param storePath where the PNG should be written to
  8. * @return true if screen shot is created successfully, false otherwise
  9. * @since API Level 17
  10. */
  11. public boolean takeScreenshot(File storePath) {
  12. Tracer.trace(storePath);
  13. return takeScreenshot(storePath, 1.0f, 90);①
  14. }
  15.  
  16. /**
  17. * Take a screenshot of current window and store it as PNG
  18. *
  19. * The screenshot is adjusted per screen rotation
  20. *
  21. * @param storePath where the PNG should be written to
  22. * @param scale scale the screenshot down if needed; 1.0f for original size
  23. * @param quality quality of the PNG compression; range: 0-100
  24. * @return true if screen shot is created successfully, false otherwise
  25. * @since API Level 17
  26. */
  27. public boolean takeScreenshot(File storePath, float scale, int quality) {
  28. Tracer.trace(storePath, scale, quality);
  29. return getAutomatorBridge().takeScreenshot(storePath, quality);②
  30. }

UiAutomatorBridge.java

  1. public boolean takeScreenshot(File storePath, int quality) {
  2. Bitmap screenshot = mUiAutomation.takeScreenshot();③
  3. if (screenshot == null) {
  4. return false;
  5. }
  6. BufferedOutputStream bos = null;
  7. try {
  8. bos = new BufferedOutputStream(new FileOutputStream(storePath));
  9. if (bos != null) {
  10. screenshot.compress(Bitmap.CompressFormat.PNG, quality, bos);
  11. bos.flush();
  12. }
  13. } catch (IOException ioe) {
  14. Log.e(LOG_TAG, "failed to save screen shot to file", ioe);
  15. return false;
  16. } finally {
  17. if (bos != null) {
  18. try {
  19. bos.close();
  20. } catch (IOException ioe) {
  21. /* ignore */
  22. }
  23. }
  24. screenshot.recycle();
  25. }
  26. return true;
  27. }

UiAutomation.java

  1. /**
  2. * Takes a screenshot.
  3. *
  4. * @return The screenshot bitmap on success, null otherwise.
  5. */
  6. public Bitmap takeScreenshot() {
  7. synchronized (mLock) {
  8. throwIfNotConnectedLocked();
  9. }
  10. Display display = DisplayManagerGlobal.getInstance()
  11. .getRealDisplay(Display.DEFAULT_DISPLAY);
  12. Point displaySize = new Point();
  13. display.getRealSize(displaySize);
  14. final int displayWidth = displaySize.x;
  15. final int displayHeight = displaySize.y;
  16.  
  17. final float screenshotWidth;
  18. final float screenshotHeight;
  19.  
  20. final int rotation = display.getRotation();
  21. switch (rotation) {
  22. case ROTATION_FREEZE_0: {
  23. screenshotWidth = displayWidth;
  24. screenshotHeight = displayHeight;
  25. } break;
  26. case ROTATION_FREEZE_90: {
  27. screenshotWidth = displayHeight;
  28. screenshotHeight = displayWidth;
  29. } break;
  30. case ROTATION_FREEZE_180: {
  31. screenshotWidth = displayWidth;
  32. screenshotHeight = displayHeight;
  33. } break;
  34. case ROTATION_FREEZE_270: {
  35. screenshotWidth = displayHeight;
  36. screenshotHeight = displayWidth;
  37. } break;
  38. default: {
  39. throw new IllegalArgumentException("Invalid rotation: "
  40. + rotation);
  41. }
  42. }
  43.  
  44. // Take the screenshot
  45. Bitmap screenShot = null;
  46. try {
  47. // Calling out without a lock held.
  48. screenShot = mUiAutomationConnection.takeScreenshot((int) screenshotWidth,
  49. (int) screenshotHeight);④
  50. if (screenShot == null) {
  51. return null;
  52. }
  53. } catch (RemoteException re) {
  54. Log.e(LOG_TAG, "Error while taking screnshot!", re);
  55. return null;
  56. }
  57.  
  58. // Rotate the screenshot to the current orientation
  59. if (rotation != ROTATION_FREEZE_0) {
  60. Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
  61. Bitmap.Config.ARGB_8888);
  62. Canvas canvas = new Canvas(unrotatedScreenShot);
  63. canvas.translate(unrotatedScreenShot.getWidth() / 2,
  64. unrotatedScreenShot.getHeight() / 2);
  65. canvas.rotate(getDegreesForRotation(rotation));
  66. canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2);
  67. canvas.drawBitmap(screenShot, 0, 0, null);
  68. canvas.setBitmap(null);
  69. screenShot = unrotatedScreenShot;
  70. }
  71.  
  72. // Optimization
  73. screenShot.setHasAlpha(false);
  74.  
  75. return screenShot;
  76. }

UiAutomationConnection.java

  1. @Override
  2. public Bitmap takeScreenshot(int width, int height) {
  3. synchronized (mLock) {
  4. throwIfCalledByNotTrustedUidLocked();
  5. throwIfShutdownLocked();
  6. throwIfNotConnectedLocked();
  7. }
  8. final long identity = Binder.clearCallingIdentity();
  9. try {
  10. return SurfaceControl.screenshot(width, height);⑤
  11. } finally {
  12. Binder.restoreCallingIdentity(identity);
  13. }
  14. }

可以看到,绕来绕去又回到方法1了。

Android 屏幕截图的更多相关文章

  1. Android 屏幕截图(底层实现方式)

    加载底层库ScreenCap.java: public class ScreenCap { static { System.loadLibrary("scrcap"); } sta ...

  2. Android 5.0之后屏幕截图的方法

    截图的几种方法 Android获取屏幕截图主要有以下三种方法 1.通过view.getDrawingCache()获取指定View的绘制缓存来实现截屏. 这种方式Android 5.0之前也可以,且不 ...

  3. Android读取/dev/graphics/fb0 屏幕截图

    Android屏幕截图有很多方式这里只使用其中一种截图 主要是读取/dev/graphics/fb0,进行转换,复杂点就在如何把读取的数据进行转换. 可以参考一下这篇文章:http://blog.ch ...

  4. 使用 HTML5 input 类型提升移动端输入体验

    在过去的几年里,在移动设备上浏览网页已变得难以置信的受欢迎. 但是这些设备上的浏览体验,有时遗留很多的有待改进.当涉及到填写表单时,这一点尤为明显.幸运的是,HTML5规范引入了许多新input类型, ...

  5. 使用 HTML5 input 类型提升移动端输入体验(键盘)

    在最近的项目中,策划老是要求我们弹出各种类型的键盘,特别是在iOS下,例如输入帐号的时候,不应该支持输入中文,该输入纯数字的时候就应该谈数字键盘等.个人觉得这些都是我们平时开发很少意识到的,虽然有些刁 ...

  6. 手机UI自动化之显示点触位置(触摸轨迹)

    上期回顾:Airtest源码分析--Android屏幕截图方式 不管是用Appium还是Airtest,或是其他手机UI自动化工具,你是不是经常遇到这种情况,代码明明执行了click或swipe,怎么 ...

  7. 使用 HTML5 input 类型提升移动端输入体验(转翻译)

    在过去的几年里,在移动设备上浏览网页已变得难以置信的受欢迎. 但是这些设备上的浏览体验,有时遗留很多的有待改进.当涉及到填写表单时,这一点尤为明显.幸运的是,HTML5规范引入了许多新input类型, ...

  8. Android 5.0之前屏幕截图的方法

    截图的几种方法 Android获取屏幕截图主要有以下三种方法 1.通过view.getDrawingCache()获取指定View的绘制缓存来实现截屏. 这种方式Android 5.0之前也可以,且不 ...

  9. Android开发 获取当前activity的屏幕截图(转载)

    首先通过下面的函数获取Bitmap格式的屏幕截图: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 pu ...

随机推荐

  1. PHP 通过随机数获得ASCII 值返回字符。

    for($i=0;$i<20;$i++){ $Matrix .= chr(65 + rand(0,26)); }

  2. Datatables+Bootstrap

    http://sandbox.runjs.cn/show/thwac3ec 运行效果 <!DOCTYPE html> <html lang="en"> &l ...

  3. xcode7 icon图标设置

  4. Objective-C 实例方法可见度,方法

    一 实例方法可见度,方法 1.实例变量的可见度 可见度                                                                       特点 ...

  5. 使用OC开发phonegp 组件

    使用OC开发phonegp 组件 1. 使用oc 对phonegp中的组件近些开发,首先具体的pgonegp跟nativecode之间的一些优劣就不说了,开发phonegp 对应的组件主要就是使用na ...

  6. RESTful互联网框架

    在我们日常接触的网络中,对于非程序员来说主要关注的就是在网上找到自己需要的资料,但是对于开发者来说,主要关注的就是将结构和页面,以及功能的分离,但是如何划分这个结构呢,或许我们知道的有MVC框架,甚至 ...

  7. 一种实现C++反射功能的想法(三)

    如何实现类型名跟类型的对应, 我们很容易想到map, 没错, 就是使用map实现的. std::map<std::string, .....>, 等下, 第二部分该填什么类型, 一个函数指 ...

  8. 用jq 做了一个排序

    <ul id="cont"> <li data="5">5</li> <li data="1"&g ...

  9. 基于jQuery查找dom的多种方式性能问题

    这个问题的产生由于我们前端组每个人的编码习惯的差异,最主要的还是因为代码的维护性问题.在此基础上,我对jQuery源码(1.11.3)查找dom节点相关的内容进行了仔细的查阅,虽然并不能理解的很深入 ...

  10. opencv之图像腐蚀

    最近准备好好学习opencv 这博客就是我的学习笔记. #include <cv.h> #include <highgui.h> using namespace std; vo ...