Capture screen shot of GoogleMap Android API V2
Asked Answered
J

8

53

Final Update

The feature request has been fulfilled by Google. Please see this answer below.

Original Question

Using the old version of the Google Maps Android API, I was able to capture a screenshot of the google map to share via social media. I used the following code to capture the screenshot and save the image to a file and it worked great:

public String captureScreen()
{
    String storageState = Environment.getExternalStorageState();
    Log.d("StorageState", "Storage state is: " + storageState);

    // image naming and path  to include sd card  appending name you choose for file
    String mPath = this.getFilesDir().getAbsolutePath();

    // create bitmap screen capture
    Bitmap bitmap;
    View v1 = this.mapView.getRootView();
    v1.setDrawingCacheEnabled(true);
    bitmap = Bitmap.createBitmap(v1.getDrawingCache());
    v1.setDrawingCacheEnabled(false);

    OutputStream fout = null;

    String filePath = System.currentTimeMillis() + ".jpeg";

    try 
    {
        fout = openFileOutput(filePath,
                MODE_WORLD_READABLE);

        // Write the string to the file
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
        fout.flush();
        fout.close();
    } 
    catch (FileNotFoundException e) 
    {
        // TODO Auto-generated catch block
        Log.d("ImageCapture", "FileNotFoundException");
        Log.d("ImageCapture", e.getMessage());
        filePath = "";
    } 
    catch (IOException e) 
    {
        // TODO Auto-generated catch block
        Log.d("ImageCapture", "IOException");
        Log.d("ImageCapture", e.getMessage());
        filePath = "";
    }

    return filePath;
}

However, the new GoogleMap object used by V2 of the api does not have a "getRootView()" method like MapView does.

I tried to do this:

    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
            .findFragmentById(R.id.basicMap);

    View v1 = mapFragment.getView();

But the screenshot that I get does not have any map content and looks like this: Blank Map Screenshot

Has anyone figured out how to take a screenshot of the new Google Maps Android API V2?

Update

I also tried to get the rootView this way:

View v1 = getWindow().getDecorView().getRootView();

This results in a screenshot that includes the action bar at the top of the screen, but the map is still blank like the screenshot I attached.

Update

A feature request has been submitted to Google. Please go star the feature request if this is something you want google to add in the future: Add screenshot ability to Google Maps API V2

Jarlath answered 8/12, 2012 at 1:51 Comment(1)
now they use vectors is what i read some where. not sure how to do it thoughSouthwestwards
J
66

Update - Google has added a snapshot method**!:

The feature request for a method to take a screen shot of the Android Google Map API V2 OpenGL layer has been fulfilled.

To take a screenshot, simply implement the following interface:

public abstract void onSnapshotReady (Bitmap snapshot)

and call:

public final void snapshot (GoogleMap.SnapshotReadyCallback callback)

Example that takes a screenshot, then presents the standard "Image Sharing" options:

public void captureScreen()
    {
        SnapshotReadyCallback callback = new SnapshotReadyCallback() 
        {

            @Override
            public void onSnapshotReady(Bitmap snapshot) 
            {
                // TODO Auto-generated method stub
                bitmap = snapshot;

                OutputStream fout = null;

                String filePath = System.currentTimeMillis() + ".jpeg";

                try 
                {
                    fout = openFileOutput(filePath,
                            MODE_WORLD_READABLE);

                    // Write the string to the file
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
                    fout.flush();
                    fout.close();
                } 
                catch (FileNotFoundException e) 
                {
                    // TODO Auto-generated catch block
                    Log.d("ImageCapture", "FileNotFoundException");
                    Log.d("ImageCapture", e.getMessage());
                    filePath = "";
                } 
                catch (IOException e) 
                {
                    // TODO Auto-generated catch block
                    Log.d("ImageCapture", "IOException");
                    Log.d("ImageCapture", e.getMessage());
                    filePath = "";
                }

                openShareImageDialog(filePath);
            }
        };

        mMap.snapshot(callback);
    }

Once the image is finished being captured, it will trigger the standard "Share Image" dialog so the user can pick how they'd like to share it:

public void openShareImageDialog(String filePath) 
{
File file = this.getFileStreamPath(filePath);

if(!filePath.equals(""))
{
    final ContentValues values = new ContentValues(2);
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
    final Uri contentUriFile = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

    final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    intent.setType("image/jpeg");
    intent.putExtra(android.content.Intent.EXTRA_STREAM, contentUriFile);
    startActivity(Intent.createChooser(intent, "Share Image"));
}
else
{
            //This is a custom class I use to show dialogs...simply replace this with whatever you want to show an error message, Toast, etc.
    DialogUtilities.showOkDialogWithText(this, R.string.shareImageFailed);
}
}

Documentation is here

Jarlath answered 7/8, 2013 at 1:41 Comment(8)
can u give an example pls!Sheepherder
@Sheepherder - example provided.Jarlath
Hi @Jarlath , this code is very usefull. but i want one solution: suppose there is one activity which contains map and some UI components. see: i57.tinypic.com/iglyef.png now i need a screenshot of this. how to do this ? Thnks in advanceRouse
@Rouse - I can't see your image, but I don't think that you can capture UI elements that are not inside your map...I think what you'd have to do is capture the map image, then, separately, capture the entire screen (the map will be blank on the entire screen capture) and then piece the 2 images back together later via some code.Jarlath
@Disc Dev. Thanks for reply...! You are right. i have to do two separate image for taking screenshot. one for entire screen and second one is only display map. and then merge both of them.There is little bit confusing here..! how can i do that ? have you some idea or reference for doing this..? Updated image location :: s13.postimg.org/5r99pibx3/test.pngRouse
It shouldn't be that hard - find how to capture the whole screen, and use the method in this answer to capture the map. Then, use some code to draw a new bitmap that has both of these over laid on top of each other - the result should be what you want.Jarlath
Hopefully not a dumb question, but what do you write for the String filePath parameter?Opinionative
MODE_WORLD_READABLE gives SecurityException now, so I used FileOutputStream to convert bitmap to File.Restoration
L
30

Below are the steps to capture screen shot of Google Map V2 with example

Step 1. open Android Sdk Manager (Window > Android Sdk Manager) then Expand Extras now update/install Google Play Services to Revision 10 ignore this step if already installed

Read Notes here https://developers.google.com/maps/documentation/android/releases#august_2013

Step 2. Restart Eclipse

Step 3. import com.google.android.gms.maps.GoogleMap.SnapshotReadyCallback;

Step 4. Make Method to Capture/Store Screen/image of Map like below

public void CaptureMapScreen() 
{
SnapshotReadyCallback callback = new SnapshotReadyCallback() {
            Bitmap bitmap;

            @Override
            public void onSnapshotReady(Bitmap snapshot) {
                // TODO Auto-generated method stub
                bitmap = snapshot;
                try {
                    FileOutputStream out = new FileOutputStream("/mnt/sdcard/"
                        + "MyMapScreen" + System.currentTimeMillis()
                        + ".png");

                    // above "/mnt ..... png" => is a storage path (where image will be stored) + name of image you can customize as per your Requirement

                    bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };

        myMap.snapshot(callback);

        // myMap is object of GoogleMap +> GoogleMap myMap;
        // which is initialized in onCreate() => 
        // myMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map_pass_home_call)).getMap();
}

Step 5. Now call this CaptureMapScreen() method where you want to capture the image

in my case i am calling this method on Button click in my onCreate() which is working fine

like:

Button btnCap = (Button) findViewById(R.id.btnTakeScreenshot);
    btnCap.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            try {
                CaptureMapScreen();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }

        }
    });

Check Doc here and here

Le answered 19/8, 2013 at 7:34 Comment(1)
I know it is a very old post, but I have a similar issue and wanted to check if you could help fix it. I am initializing myMap like this: myMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap(); I am getting the following error: txs.io/JBS and here is the map_pass_home_call.xml file that I have: txs.io/KBS. I am not sure where I am going wrong.. I am always getting null pointer exception.Indenture
S
6

I capctured Map screenshot.It will be helpful

  private GoogleMap map;
 private static LatLng latLong;

`

public void onMapReady(GoogleMap googleMap) {
           map = googleMap;
           setMap(this.map);
           animateCamera();
            map.moveCamera (CameraUpdateFactory.newLatLng (latLong));
            map.setOnMapLoadedCallback (new GoogleMap.OnMapLoadedCallback () {
                @Override
                public void onMapLoaded() {
                    snapShot();
                }
            });
        }

`

snapShot() method for taking screenshot of map

 public void snapShot(){
    GoogleMap.SnapshotReadyCallback callback=new GoogleMap.SnapshotReadyCallback () {
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) {
            bitmap=snapshot;

            try{
                file=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"map.png");
                FileOutputStream fout=new FileOutputStream (file);
                bitmap.compress (Bitmap.CompressFormat.PNG,90,fout);
                Toast.makeText (PastValuations.this, "Capture", Toast.LENGTH_SHORT).show ();

            }catch (Exception e){
                e.printStackTrace ();
                Toast.makeText (PastValuations.this, "Not Capture", Toast.LENGTH_SHORT).show ();
            }


        }
    };map.snapshot (callback);
}

My output is belowenter image description here

Stooge answered 8/8, 2018 at 14:35 Comment(1)
This is working well, it is saving the picture in Pictures folder..Joe
A
4

Edit: this answer is no longer valid - the feature request for screenshots on Google Maps Android API V2 has been fulfilled. See this answer for an example.

Original Accepted Answer

Since the new Android API v2 Maps are displayed using OpenGL, there are no possibilities to create a screenshot.

Aggappora answered 10/12, 2012 at 13:13 Comment(9)
Can you cite a source for this answer?Jarlath
I got this mainly from the official API docs in addition with this postAggappora
I've seen questions like this: #3311490 though, which seem to suggest it is possible to take a screenshot of an OpenGL layer...although I know nearly nothing about OpenGL so that's why I'm looking for help...Jarlath
The problem is that the OpenGL SurfaceView, which is used to display the map, is blackboxed away through the MapView/MapFragment...Aggappora
spotdog13 you found the solution of this problem?Donate
There is no solution. Due to the OpenGL layer of the map not being accessible, it is not possible to capture a screenshot using conventional methods via code. You can of course use DDMS to capture a screen shot while developing, but you cannot do it programmatically at this time. Google fail.Jarlath
You can upvote/star the following feature request for Google to add screenshot support in the API: code.google.com/p/gmaps-api-issues/issues/detail?id=4898Phlegethon
is there a solution to this yet?Dematerialize
Update - the feature request has been fulfilled. Please see my answer above for the methods you can use to take a screenshot: https://mcmap.net/q/337854/-capture-screen-shot-of-googlemap-android-api-v2Jarlath
L
4

Since the top voted answer doesnt work with polylines and other overlays on top of the map fragment (What I was looking for), I want to share this solution.

public void captureScreen()
        {
            GoogleMap.SnapshotReadyCallback callback = new GoogleMap.SnapshotReadyCallback()
            {


                @Override
                public void onSnapshotReady(Bitmap snapshot) {
                    try {
                        getWindow().getDecorView().findViewById(android.R.id.content).setDrawingCacheEnabled(true);
                        Bitmap backBitmap = getWindow().getDecorView().findViewById(android.R.id.content).getDrawingCache();
                        Bitmap bmOverlay = Bitmap.createBitmap(
                                backBitmap.getWidth(), backBitmap.getHeight(),
                                backBitmap.getConfig());
                        Canvas canvas = new Canvas(bmOverlay);
                        canvas.drawBitmap(snapshot, new Matrix(), null);
                        canvas.drawBitmap(backBitmap, 0, 0, null);

                        OutputStream fout = null;

                        String filePath = System.currentTimeMillis() + ".jpeg";

                        try
                        {
                            fout = openFileOutput(filePath,
                                    MODE_WORLD_READABLE);

                            // Write the string to the file
                            bmOverlay.compress(Bitmap.CompressFormat.JPEG, 90, fout);
                            fout.flush();
                            fout.close();
                        }
                        catch (FileNotFoundException e)
                        {
                            // TODO Auto-generated catch block
                            Log.d("ImageCapture", "FileNotFoundException");
                            Log.d("ImageCapture", e.getMessage());
                            filePath = "";
                        }
                        catch (IOException e)
                        {
                            // TODO Auto-generated catch block
                            Log.d("ImageCapture", "IOException");
                            Log.d("ImageCapture", e.getMessage());
                            filePath = "";
                        }

                        openShareImageDialog(filePath);


                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };

           ;


            map.snapshot(callback);
        }
Loathing answered 16/7, 2015 at 3:29 Comment(1)
The accepted answer most definitely works with polylines and overlays - that was the whole point of sharing the map image, because I draw a bunch of poly lines and markers and overlays and wanted to capture it. Perhaps there is a problem with the way you're adding the polylines to your map?Jarlath
K
2
private GoogleMap mMap;
SupportMapFragment mapFragment;
LinearLayout linearLayout;
String jobId="1";

File file;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate (savedInstanceState);
    setContentView (R.layout.activity_maps);

    linearLayout=(LinearLayout)findViewById (R.id.linearlayout);
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
     mapFragment = (SupportMapFragment)getSupportFragmentManager ()
            .findFragmentById (R.id.map);
    mapFragment.getMapAsync (this);
    //Taking Snapshot of Google Map


}



/**
 * Manipulates the map once available.
 * This callback is triggered when the map is ready to be used.
 * This is where we can add markers or lines, add listeners or move the camera. In this case,
 * we just add a marker near Sydney, Australia.
 * If Google Play services is not installed on the device, the user will be prompted to install
 * it inside the SupportMapFragment. This method will only be triggered once the user has
 * installed Google Play services and returned to the app.
 */
@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;

    // Add a marker in Sydney and move the camera
    LatLng sydney = new LatLng (-26.888033, 75.802754);
    mMap.addMarker (new MarkerOptions ().position (sydney).title ("Kailash Tower"));
    mMap.moveCamera (CameraUpdateFactory.newLatLng (sydney));
    mMap.setOnMapLoadedCallback (new GoogleMap.OnMapLoadedCallback () {
        @Override
        public void onMapLoaded() {
            snapShot();
        }
    });
}

// Initializing Snapshot Method
public void snapShot(){
    GoogleMap.SnapshotReadyCallback callback=new GoogleMap.SnapshotReadyCallback () {
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) {
            bitmap=snapshot;
            bitmap=getBitmapFromView(linearLayout);
            try{
               file=new File (getExternalCacheDir (),"map.png");
                FileOutputStream fout=new FileOutputStream (file);
                bitmap.compress (Bitmap.CompressFormat.PNG,90,fout);
                Toast.makeText (MapsActivity.this, "Capture", Toast.LENGTH_SHORT).show ();
                sendSceenShot (file);
            }catch (Exception e){
                e.printStackTrace ();
                Toast.makeText (MapsActivity.this, "Not Capture", Toast.LENGTH_SHORT).show ();
            }


        }
    };mMap.snapshot (callback);
}
private Bitmap getBitmapFromView(View view) {
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas (returnedBitmap);
    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);
    }
    view.draw(canvas);
    return returnedBitmap;
}

//Implementing Api using Retrofit
private void sendSceenShot(File file) {
    RequestBody job=null;

    Gson gson = new GsonBuilder ()
            .setLenient ()
            .create ();

    Retrofit retrofit = new Retrofit.Builder ()
            .baseUrl (BaseUrl.url)
            .addConverterFactory (GsonConverterFactory.create (gson))
            .build ();

    final RequestBody requestBody = RequestBody.create (MediaType.parse ("image/*"),file);
    job=RequestBody.create (MediaType.parse ("text"),jobId);


    MultipartBody.Part  fileToUpload = MultipartBody.Part.createFormData ("name",file.getName (), requestBody);

    API service = retrofit.create (API.class);
    Call<ScreenCapture_Pojo> call=service.sendScreen (job,fileToUpload);
    call.enqueue (new Callback<ScreenCapture_Pojo> () {
        @Override
        public void onResponse(Call <ScreenCapture_Pojo> call, Response<ScreenCapture_Pojo> response) {
            if (response.body ().getMessage ().equalsIgnoreCase ("Success")){
                Toast.makeText (MapsActivity.this, "success", Toast.LENGTH_SHORT).show ();
            }
        }

        @Override
        public void onFailure(Call <ScreenCapture_Pojo> call, Throwable t) {

        }
    });

}

}

Ketty answered 30/5, 2018 at 7:41 Comment(4)
Hello guy, Successful Code.Ketty
I hope this would help to capture the screenshot of your map and you can send on server using retrofit without save into External Storage. please comment .Ketty
Example that takes a screenshot, then presents the standard "Image sending . this full source code. please subscribe my page.Ketty
Will I take a screenshot if a particular view which contain an ImageView, TextView and a mapView(all with values)?Sophey
S
1

I hope this would help to capture the screenshot of your map

Method call:

gmap.setOnMapLoadedCallback(mapLoadedCallback);

Method declaration:

final SnapshotReadyCallback snapReadyCallback = new SnapshotReadyCallback() {
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) {
            bitmap = snapshot;

            try {

                //do something with your snapshot

                imageview.setImageBitmap(bitmap);

            } catch (Exception e) {
                e.printStackTrace();
            }


        }
    };

GoogleMap.OnMapLoadedCallback mapLoadedCallback = new GoogleMap.OnMapLoadedCallback() {
        @Override
        public void onMapLoaded() {
            gmap.snapshot(snapReadyCallback);
        }
};
Spiteful answered 22/2, 2018 at 18:9 Comment(0)
C
-2

Eclipse DDMS can capture the screen even it's google map V2.

Try to call /system/bin/screencap or /system/bin/screenshot if you have the "root". I learned that from How Eclipse android DDMS implement "screen capture"

Corruptible answered 6/2, 2013 at 9:1 Comment(3)
Yes, I'm aware of that, but I want to be able to take a screenshot programmatically (i.e. in code) on a non-rooted device. This was possible in the first version of the Maps API for Android, but not the second (due to reasons mentioned in the accepted answer).Jarlath
Yes, this is also my problem. But it seems not possible if google map V2. DDMS use the screencap or screensnap android provided via a process (adb pull) which has the necessary authority. But normal android app do not the authority(I find from stackoverflow yesterday ). Hope the next version of Google map will enhance it.Corruptible
Yes, it's frustrating that they don't make the OpenGL layer available, but I imagine it has something to do with copyright or security.Jarlath

© 2022 - 2024 — McMap. All rights reserved.