51

Possible Duplicate:
How to programatically take a screenshot on Android?

How to capture the android device screen content and make an image file using the snapshot data? Which API should I use or where could I find related resources?

BTW: not camera snapshot, but device screen

Community
  • 1
  • 1
Jagie
  • 2,190
  • 3
  • 27
  • 25

8 Answers8

43

Use the following code:

Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);

Here MyView is the View through which we need include in the screen. You can also get DrawingCache from of any View this way (without getRootView()).


There is also another way..
If we having ScrollView as root view then its better to use following code,

LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
FrameLayout root = (FrameLayout) inflater.inflate(R.layout.activity_main, null); // activity_main is UI(xml) file we used in our Activity class. FrameLayout is root view of my UI(xml) file.
root.setDrawingCacheEnabled(true);
Bitmap bitmap = getBitmapFromView(this.getWindow().findViewById(R.id.frameLayout)); // here give id of our root layout (here its my FrameLayout's id)
root.setDrawingCacheEnabled(false);

Here is the getBitmapFromView() method

public static Bitmap getBitmapFromView(View view) {
        //Define a bitmap with the same size as the view
        Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
        //Bind a canvas to it
        Canvas canvas = new Canvas(returnedBitmap);
        //Get the view's background
        Drawable bgDrawable =view.getBackground();
        if (bgDrawable!=null) 
            //has background drawable, then draw it on the canvas
            bgDrawable.draw(canvas);
        else 
            //does not have background drawable, then draw white background on the canvas
            canvas.drawColor(Color.WHITE);
        // draw the view on the canvas
        view.draw(canvas);
        //return the bitmap
        return returnedBitmap;
    }

It will display entire screen including content hidden in your ScrollView


UPDATED AS ON 20-04-2016

There is another better way to take screenshot.
Here I have taken screenshot of WebView.

WebView w = new WebView(this);
    w.setWebViewClient(new WebViewClient()
    {
        public void onPageFinished(final WebView webView, String url) {

            new Handler().postDelayed(new Runnable(){
                @Override
                public void run() {
                    webView.measure(View.MeasureSpec.makeMeasureSpec(
                                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
                            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                    webView.layout(0, 0, webView.getMeasuredWidth(),
                            webView.getMeasuredHeight());
                    webView.setDrawingCacheEnabled(true);
                    webView.buildDrawingCache();
                    Bitmap bitmap = Bitmap.createBitmap(webView.getMeasuredWidth(),
                            webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

                    Canvas canvas = new Canvas(bitmap);
                    Paint paint = new Paint();
                    int height = bitmap.getHeight();
                    canvas.drawBitmap(bitmap, 0, height, paint);
                    webView.draw(canvas);

                    if (bitmap != null) {
                        try {
                            String filePath = Environment.getExternalStorageDirectory()
                                    .toString();
                            OutputStream out = null;
                            File file = new File(filePath, "/webviewScreenShot.png");
                            out = new FileOutputStream(file);

                            bitmap.compress(Bitmap.CompressFormat.PNG, 50, out);
                            out.flush();
                            out.close();
                            bitmap.recycle();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, 1000);
        }
    });

Hope this helps..!

Nirav Dangi
  • 3,607
  • 4
  • 49
  • 60
  • This way just helps you to capture your app, right? What if I want to capture entire screen? – Nguyen Minh Binh Aug 30 '12 at 05:00
  • 2
    Unfortunately, this does not appear to work when capturing things like dialogs and when the keyboard is showing. :( – LadyCailin Nov 02 '12 at 23:11
  • Android Studio is giving me error: "not able to find method `getBitmapFromView`", what method is that? – DroidDev Jun 03 '14 at 06:26
  • for anybody else who is wondering same, [here](http://stackoverflow.com/a/9595919/2389078) is `getBitmapFromView` method. – DroidDev Jun 03 '14 at 06:48
  • This method works well for software layer views. But it doesn't work for videos (VideoView, or a video in Webview). All other pixels will be captured, but the video only shows black. I think the reason is that we can get software layer bitmap, but cannot get the bitmap of video. Anyone knows how to take screenshot of a Webview with video? – user2060386 Apr 25 '15 at 23:53
  • @user2060386 I have updated my answer please check. Hope this helps to you ! – Nirav Dangi Apr 27 '15 at 06:23
  • @NiravDangi, Thanks for update! But this still doesn't work for a webview with video (like a youtube page). Other static parts of the page is captured well, but the video is black. – user2060386 Apr 27 '15 at 06:59
  • Unable to capture entire page like scrollview/recyclerview mentioned above. – Robert Feb 15 '16 at 10:46
  • @user2060386 Can you please check now? I have updated my answer. I have added handler inside `onPageFinished()` method having 1-second post delay. So, probably you may require increasing the post delay if it not works. – Nirav Dangi Apr 20 '16 at 12:25
  • i want current any screen during start mobile and other device so plase – Ramani Hitesh Jan 01 '18 at 07:29
23

For newer Android platforms, one can execute a system utility screencap in /system/bin to get the screenshot without root permission. You can try /system/bin/screencap -h to see how to use it under adb or any shell.

By the way, I think this method is only good for single snapshot. If we want to capture multiple frames for screen play, it will be too slow. I don't know if there exists any other approach for a faster screen capture.

Vishal Yadav
  • 3,642
  • 3
  • 25
  • 42
Ducky Chen
  • 369
  • 2
  • 5
  • on ICS and on Honeycomb, works like charm. – Alex Cohn Aug 02 '12 at 07:12
  • /system/bin/screencap is great but seems to force a max read rate of 3 screens per second :( – Rui Marques Sep 27 '12 at 16:14
  • @AlexCohn: could you please post the sample code. Thanks – Nam Vu Nov 05 '12 at 11:49
  • @ZuzooVn: this is a utility (command), no code for this. – Alex Cohn Nov 14 '12 at 09:28
  • @AlexCohn : without root permission? . How can you run command – Nam Vu Nov 14 '12 at 16:25
  • The idea is to run the command from the ADB shell. You can also run it from a terminal window on device, but then you may not have enough permissions. – Alex Cohn Nov 14 '12 at 16:28
  • 4
    It runs great from the adb shell. How do I get it to run from code? This does not work: String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "outhope.png"; Process proc = Runtime.getRuntime().exec( "/system/bin/screencap -p " + path); – gregm Jan 25 '13 at 18:08
  • Run from code using [RunAsRoot](http://stackoverflow.com/questions/6882248/running-shell-commands-though-java-code-on-android). Works for me. – barkside Feb 15 '13 at 09:06
  • @barkside: what Andriod version are you using? I get a permission denied. – ocramot Sep 05 '14 at 14:31
  • @ocramot This was on 4.1.2 Cyanogenmod – barkside Sep 05 '14 at 15:04
  • The source code of screencap could be found in AOSP source tree. – Ducky Chen Sep 25 '14 at 07:27
  • 1
    The applications which capture the screen need higher privileges. This is for security issue (preventing from backdoor). As the result, even you recompile the source code to have a new app, you cannot get expected result unless you run it as root. – Ducky Chen Sep 25 '14 at 07:32
  • Root is not really required if it is signed with platform builder. – Ducky Chen Sep 25 '14 at 08:07
23

AFAIK, All of the methods currently to capture a screenshot of android use the /dev/graphics/fb0 framebuffer. This includes ddms. It does require root to read from this stream. ddms uses adbd to request the information, so root is not required as adb has the permissions needed to request the data from /dev/graphics/fb0.

The framebuffer contains 2+ "frames" of RGB565 images. If you are able to read the data, you would have to know the screen resolution to know how many bytes are needed to get the image. each pixel is 2 bytes, so if the screen res was 480x800, you would have to read 768,000 bytes for the image, since a 480x800 RGB565 image has 384,000 pixels.

Ryan Conrad
  • 6,870
  • 2
  • 36
  • 36
  • Hi Ryan you are absolutely right,i really appreciate your answer.. can please give me a sample code describing how to capture screen from /dev/graphics/fb0.. – manju Jan 10 '11 at 11:10
  • 5
    Note: not all framebuffers are RGB565. There are different formats. – ACT Dec 15 '10 at 21:42
  • yes this is the only way to go, that doesn't require root – radhoo Apr 03 '12 at 11:46
  • Ryan, would you please elaborate more on exactly what happens that makes ADBD have access to the frame buffer? I de-compiled DDMS code and tried to package the DDMS code for screen capture on the device itself (as part of an app), but i didn't know how that can get access to permissions for frame buffer. – David T. May 30 '12 at 22:21
  • adbd runs on the device, it allows the adb client (ddms) to connect and request the framebuffer. I don't know if it will work with an app running directly on the device. DDMS connects to adbd via tcp. The adbd process runs as a "root" account, which is why it can access the framebuffer. – Ryan Conrad Jun 01 '12 at 01:57
  • 1
    This answer is incorrect and out of date as far as Honeycomb and onwards is concerned. You're not guaranteed that all screen content will end up in fb0 as more direct rendering methods maybe used such as hardware overlays. Best off looking at the answer provided by Ducky Chen. – tonylo Sep 14 '12 at 16:25
  • I think this http://www.pocketmagic.net/2010/11/android-native-screen-capture-application-using-the-framebuffer/#.Ue5vj32XsUQ must be helpful for the same – Megharaj Jul 23 '13 at 12:34
  • This one was quite helpful but I think you made a mistake with each pixel is 2 bytes. As per http://developer.android.com/reference/android/graphics/Color.html Colors are represented as packed ints, made up of 4 bytes: alpha, red, green, blue. So the format of the pixels in the buffer is actually RGBA so you need to read 4 bytes. For exampel to get red from first pixel do final int red = ((colorBytes[0] & 0xFF)); As @ACT also mentioned this might be different on different devices. – Igor Čordaš May 19 '15 at 14:02
  • @PSIXO that is true with some other formats, when I wrote this answer, the format that was most common was RGB565 which is a 16-bit color format. http://www.theimagingsource.com/en_US/support/documentation/icimagingcontrol-class/PixelformatRGB565.htm – Ryan Conrad May 19 '15 at 16:33
  • But where is solution ? This is not code or exact solution. – Anand Savjani Jul 31 '15 at 10:54
  • I'm not here to write code for you. I post to help solve a problem. – Ryan Conrad Jul 31 '15 at 12:14
18

[Based on Android source code:]

At the C++ side, the SurfaceFlinger implements the captureScreen API. This is exposed over the binder IPC interface, returning each time a new ashmem area that contains the raw pixels from the screen. The actual screenshot is taken through OpenGL.

For the system C++ clients, the interface is exposed through the ScreenshotClient class, defined in <surfaceflinger_client/SurfaceComposerClient.h> for Android < 4.1; for Android > 4.1 use <gui/SurfaceComposerClient.h>

Before JB, to take a screenshot in a C++ program, this was enough:

ScreenshotClient ssc;
ssc.update();

With JB and multiple displays, it becomes slightly more complicated:

ssc.update(
    android::SurfaceComposerClient::getBuiltInDisplay(
        android::ISurfaceComposer::eDisplayIdMain));

Then you can access it:

do_something_with_raw_bits(ssc.getPixels(), ssc.getSize(), ...);

Using the Android source code, you can compile your own shared library to access that API, and then expose it through JNI to Java. To create a screen shot form your app, the app has to have the READ_FRAME_BUFFER permission. But even then, apparently you can create screen shots only from system applications, i.e. ones that are signed with the same key as the system. (This part I still don't quite understand, since I'm not familiar enough with the Android Permissions system.)

Here is a piece of code, for JB 4.1 / 4.2:

#include <utils/RefBase.h>
#include <binder/IBinder.h>
#include <binder/MemoryHeapBase.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>

static void do_save(const char *filename, const void *buf, size_t size) {
    int out = open(filename, O_RDWR|O_CREAT, 0666);
    int len = write(out, buf, size);
    printf("Wrote %d bytes to out.\n", len);
    close(out);
}

int main(int ac, char **av) {
    android::ScreenshotClient ssc;
    const void *pixels;
    size_t size;
    int buffer_index;

    if(ssc.update(
        android::SurfaceComposerClient::getBuiltInDisplay(
            android::ISurfaceComposer::eDisplayIdMain)) != NO_ERROR ){
        printf("Captured: w=%d, h=%d, format=%d\n");
        ssc.getWidth(), ssc.getHeight(), ssc.getFormat());
        size = ssc.getSize();
        do_save(av[1], pixels, size);
    }
    else
        printf(" screen shot client Captured Failed");
    return 0;
}
amIT
  • 664
  • 13
  • 27
Pekka Nikander
  • 1,585
  • 16
  • 15
  • is it possible to implement the same in android application. – Megharaj Jul 26 '13 at 12:10
  • this works perfectly would like to add an error check in update method though,I want to add a extra comments for other users . In case you want get the screen content at different resolution (downscaled /upscaled ) just give those as parameter while calling the "update" function , like this ssc.update(displayID,width,height) – amIT Feb 20 '14 at 11:08
  • eclipse is showing me Invalid arguments 'Candidates are:void do_save(const char *, const void *, ?) ' any help with that ? – r4jiv007 Jul 15 '14 at 07:16
  • I tried compiling this and I cant get it to work - and I've invested quuuuite a bit of time. First of, most of the includes are in different repos of the Android Source Code. And now it does not compile! Tried it on Cygwin, but stdatomic.h is missing and with gcc-4.9 it is there, but errors are all over the place. PLEASE provide a minimal working example and some hints! – ThE_-_BliZZarD Oct 15 '14 at 01:10
  • Note that these instructions are for a *system* application. You cannot easily use the code with the NDK or within an installable application. Usually you build system applications as part of the Android itself, e.g. as when you compile AOSP or Cyanogenmod. To use these instructions with the NDK, you have to copy the relevant sources form AOSP, and build your own compilation environment from those. I haven't tried that myself, I did a system app instead. – Pekka Nikander Oct 15 '14 at 18:09
  • How can this be expanded so that it also captures the SurfaceView contents? Currently I get black/empty sections wherever I have SurfaceViews in my layout. – Kiran Parmar Feb 01 '15 at 10:03
  • @KiranParmar, what version of Android you are using? Which EGL version? In newer Androids, EGL has much larger role than before. For example, SurfaceViews are separate SurfaceFlinger windows where usually OpenGL is used to draw directly to the screen. Based on your comment it sounds like that the `ScreenshotClient` hasn't been updated to handle direct OpenGL windows, such as those used for SurfaceViews. I would assume the same applies also for media player windows, but I haven't checked. – Pekka Nikander Mar 15 '15 at 06:36
  • @PekkaNikander: I'm on Android 4.3. I don't know about the EGL version Actually, I was able to get the surfaceview capture through fb1 instead of fb0 since this is a customized version of Android & I didn't know about this internal modification. All I had to do was cat fb1 > some_file & pull this file & transform it from custom format into png/jpg using ffmpeg :) – Kiran Parmar Mar 16 '15 at 06:50
15

You can try the following library: Android Screenshot Library (ASL) enables to programmatically capture screenshots from Android devices without requirement of having root access privileges. Instead, ASL utilizes a native service running in the background, started via the Android Debug Bridge (ADB) once per device boot.

Vishal Yadav
  • 3,642
  • 3
  • 25
  • 42
Kuba
  • 878
  • 7
  • 10
  • Any example to take the screen shot from this library ??? – Shreyash Mahajan Oct 14 '11 at 04:31
  • 1
    the indicated project has too components: a native framebuffer grabber and a java interface, the native component still needs ROOT to run! so overall this solution will not work without root. – radhoo Apr 03 '12 at 11:45
13

According to this link, it is possible to use ddms in the tools directory of the android sdk to take screen captures.

To do this within an application (and not during development), there are also applications to do so. But as @zed_0xff points out it certainly requires root.

CJBS
  • 15,147
  • 6
  • 86
  • 135
Joubarc
  • 1,206
  • 10
  • 17
  • it's changed a little ddms in the tools directory says to run monitor. Monitor has a neat icon for taking pictures – danny117 Sep 21 '13 at 00:10
  • Agreed, this answer is now severely outdated, especially considering a lot of devices even offer this functionality out of the box (such as Power+VolDown on a Nexus device). I think you could post your comment as a full answer, but I have some doubts the original asker will change the accepted answer (this one most certainly isn't the best answer now) – Joubarc Sep 21 '13 at 18:21
  • i want current any screen during start mobile and other device so please let me know how is there – Ramani Hitesh Jan 01 '18 at 08:48
  • link is dead please fix – Dominic Cerisano Aug 17 '23 at 16:48
7

Framebuffer seems the way to go, it will not always contain 2+ frames like mentioned by Ryan Conrad. In my case it contained only one. I guess it depends on the frame/display size.

I tried to read the framebuffer continuously but it seems to return for a fixed amount of bytes read. In my case that is (3 410 432) bytes, which is enough to store a display frame of 854*480 RGBA (3 279 360 bytes). Yes, the frame in binary outputed from fb0 is RGBA in my device. This will most likely depend from device to device. This will be important for you to decode it =)

In my device /dev/graphics/fb0 permissions are so that only root and users from group graphics can read the fb0. graphics is a restricted group so you will probably only access fb0 with a rooted phone using su command.

Android apps have the user id (uid) app_## and group id (guid) app_## .

adb shell has uid shell and guid shell, which has much more permissions than an app. You can actually check those permissions at /system/permissions/platform.xml

This means you will be able to read fb0 in the adb shell without root but you will not read it within the app without root.

Also, giving READ_FRAME_BUFFER and/or ACCESS_SURFACE_FLINGER permissions on AndroidManifest.xml will do nothing for a regular app because these will only work for 'signature' apps.

Rui Marques
  • 8,567
  • 3
  • 60
  • 91
  • How did you discovery that you framebuffer format is RGBA? – Guilherme Torres Castro Mar 18 '13 at 01:08
  • I opened a raw framebuffer image with GIMP. When I set it to open as RGB, the image was not ok, when I set it as RGBA - it was ok ;) – Rui Marques Mar 18 '13 at 09:44
  • Can you point out an example of capturing image through framebuffer? I'm working on the Android source-code, so rooting/permission is not an issue for me. Will this also work for SurfaceView? I tried out some examples from the internet, but nothing's worked so far for the surface-view... I did try using the screencap as well, but it also returns me black/empty part where I have SurfaceViews present in the layout – Kiran Parmar Feb 01 '15 at 10:02
5

if you want to do screen capture from Java code in Android app AFAIK you must have Root provileges.

zed_0xff
  • 32,417
  • 7
  • 53
  • 72