How to capture the android device screen content? [duplicate]
Asked Answered
S

8

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

Sabian answered 18/6, 2010 at 6:22 Comment(0)
J
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.

Janka answered 18/6, 2010 at 9:2 Comment(4)
it's changed a little ddms in the tools directory says to run monitor. Monitor has a neat icon for taking picturesEnwrap
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)Janka
i want current any screen during start mobile and other device so please let me know how is therePriestcraft
link is dead please fixTwoedged
C
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..!

Cartierbresson answered 14/12, 2011 at 13:4 Comment(10)
This way just helps you to capture your app, right? What if I want to capture entire screen?Patty
Unfortunately, this does not appear to work when capturing things like dialogs and when the keyboard is showing. :(Espinal
Android Studio is giving me error: "not able to find method getBitmapFromView", what method is that?Hastate
for anybody else who is wondering same, here is getBitmapFromView method.Hastate
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?Orian
@Orian I have updated my answer please check. Hope this helps to you !Cartierbresson
@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.Orian
Unable to capture entire page like scrollview/recyclerview mentioned above.Selfseeker
@Orian 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.Cartierbresson
i want current any screen during start mobile and other device so plasePriestcraft
A
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.

Aplenty answered 18/6, 2010 at 13:21 Comment(11)
Note: not all framebuffers are RGB565. There are different formats.Interfluve
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..Lindblad
yes this is the only way to go, that doesn't require rootVancevancleave
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.Stinkpot
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.Aplenty
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.Lathrope
I think this pocketmagic.net/2010/11/… must be helpful for the sameAttribution
This one was quite helpful but I think you made a mistake with each pixel is 2 bytes. As per 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 @Interfluve also mentioned this might be different on different devices.Erb
@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. theimagingsource.com/en_US/support/documentation/…Aplenty
But where is solution ? This is not code or exact solution.Interpellation
I'm not here to write code for you. I post to help solve a problem.Aplenty
T
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.

Turpin answered 11/6, 2012 at 9:50 Comment(13)
on ICS and on Honeycomb, works like charm.Oporto
/system/bin/screencap is great but seems to force a max read rate of 3 screens per second :(Oenomel
@AlexCohn: could you please post the sample code. ThanksHewes
@ZuzooVn: this is a utility (command), no code for this.Oporto
@AlexCohn : without root permission? . How can you run commandHewes
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.Oporto
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);Rustle
Run from code using RunAsRoot. Works for me.Aver
@barkside: what Andriod version are you using? I get a permission denied.Ewer
@Ewer This was on 4.1.2 CyanogenmodAver
The source code of screencap could be found in AOSP source tree.Turpin
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.Turpin
Root is not really required if it is signed with platform builder.Turpin
A
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;
}
Ancona answered 2/11, 2011 at 10:55 Comment(8)
is it possible to implement the same in android application.Attribution
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)Alcoholometer
eclipse is showing me Invalid arguments 'Candidates are:void do_save(const char *, const void *, ?) ' any help with that ?Josephson
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!Edify
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.Ancona
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.Ineligible
@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.Ancona
@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 :)Ineligible
Z
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.

Zandrazandt answered 7/9, 2010 at 14:6 Comment(2)
Any example to take the screen shot from this library ???Catenary
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.Vancevancleave
J
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.

Janka answered 18/6, 2010 at 9:2 Comment(4)
it's changed a little ddms in the tools directory says to run monitor. Monitor has a neat icon for taking picturesEnwrap
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)Janka
i want current any screen during start mobile and other device so please let me know how is therePriestcraft
link is dead please fixTwoedged
O
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.

Oenomel answered 31/8, 2012 at 18:12 Comment(3)
How did you discovery that you framebuffer format is RGBA?Chromite
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 ;)Oenomel
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 layoutIneligible
I
5

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

Idio answered 18/6, 2010 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.